csu1965—Message(斯坦纳树)

题目链接:传送门

1965: Message

            Time Limit: 1 Sec       Memory Limit: 128 Mb       Submitted: 68       Solved: 7    

Description

You are a boss who has N employers. One day, you have a message and you want to tell it to M employers of yours. As you are a funny man, you want to choose some of the M employers and tell them the message, and then let them tell other employers so that all the M know the message (you don't care whether the other N − M employers know the message or not). Employers who know the message can tell it to anyone else. The ith employer telling the jth employer causes a cost of Cij and you want to know what the minimal total cost is.

Input

The input data consists of several cases. The first line contains one integer, T, indicating the number of test cases. For each test case, the first line contains two integers, N (10 ≤ N ≤ 50) and M (5 ≤ M ≤ 10), described above. The employers are numbered from 1. In the next N lines, each line contains N integers. The jth integer in the ith line is Cij (1 ≤ Cij ≤ 1000Cii = 0Cij = Cji). In the next line, there are 2 × M integers. The 2 × i − 1th integer is the index of the ith employer who you want to know the message. The 2 × ith is the cost for you to tell him the message. All cost values are between 1 and 1000.

Output

For each test case, print one integer in one line, W, indicating the mininal total cost.

Sample Input

1
10 5
0 42 468 501 479 706 492 605 719 668
42 0 335 170 359 146 996 903 896 300
468 335 0 725 963 282 943 154 448 36
501 170 725 0 465 828 828 293 727 895
479 359 963 465 0 962 437 383 772 704
706 146 282 828 962 0 392 422 539 812
492 996 943 828 437 392 0 717 870 323
605 903 154 293 383 422 717 0 913 334
719 896 448 727 772 539 870 913 0 674
668 300 36 895 704 812 323 334 674 0
5 142 2 54 9 148 3 158 8 60

Sample Output

558


解题思路:斯坦纳树模板


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <stack>
#include <queue>
#include <set>
#include <map>

using namespace std;

#define move(a)(1<<(a))

typedef long long ll;
typedef pair<int,int>PA;
const int N = 100900;
const int M = 60;
const int INF = 0x3fffffff;
const double eps = 1e-8;
const double PI = acos(-1.0);
const int mod = 1001113;

int dp[1<<15][M],dis[M][M];
int st[12];   //存储boss想要哪些员工知道的编号

//斯坦纳树:在平面中给你n个点,问你其中m个点到点p的最小生成树,一般n<=10

//dp[M个必须知道消息的人组成的状态][到老板或某个员工]:最小花费
//将老板看做0点,最后的答案就是dp[(1<<m)-1][0]
//dp[i][j]的松弛:
//dp[i][j] = min{dp[i][j],dp[k][i]+dp[l][i]},其中k和l是对j的一个划分
//dp[i][j] = min{dp[i][j],dp[i][j']+dist[j'][j]},j与j'有边相连

void cal( int n , int m )
{
    //初始化
	for( int sta = 0 ; sta < move(n) ; ++sta ){
		for( int i = 0 ; i <= n+m ; ++i )
			dp[sta][i] = INF;
	}
	for( int i = 0 ; i < n ; ++i ){
		for( int j = 0 ; j <= n+m ; ++j )
            //n个人中的第i个人到第j号人的花费就是st[i]到j间的最小花费
			dp[move(i)][j] = dis[st[i]][j];
	}

    //状态松弛
	for( int sta = 1 ; sta < move(n) ; ++sta ){
		//是否有子集
		if( sta&(sta-1) ){
			for( int i = 0 ; i <= n+m ; ++i ){
				for( int j = sta ; j > 0 ; j = (j-1)&sta ){
                    if( dp[sta][i] > dp[sta^j][i]+dp[j][i] )
						dp[sta][i] = dp[sta^j][i]+dp[j][i];
				}
			}
			for( int i = 0 ; i <= n+m ; ++i ){
				for( int j = 0 ; j <= n+m ; ++j )
					if( dp[sta][i] > dp[sta][j]+dis[j][i] )
						dp[sta][i] = dp[sta][j]+dis[j][i];
			}
		}
	}
}

void floyed( int n )
{
    for( int k = 0 ; k <= n ; ++k ){
        for( int i = 0 ; i <= n ; ++i ){
            for( int j = 0 ; j <= n ; ++j ){
                dis[i][j] = min( dis[i][j] , dis[i][k]+dis[k][j] );
            }
        }
    }
}

int main()
{
//    freopen("1.in","r",stdin);
//    freopen("yuan.out","w",stdout);
    int T;
    scanf("%d",&T);
    while( T-- ){
        memset( dis , -1 , sizeof(dis) );
        int n,m;
        scanf("%d%d",&n,&m);
        for( int i = 1 ; i <= n ; ++i ){
            for( int j = 1 ; j <= n ; ++j ){
                scanf("%d",&dis[i][j]);
            }
        }
        int a,b;
        for( int i = 0 ; i < m ; ++i ){
            scanf("%d%d",&a,&b);
            dis[0][a] = dis[a][0] = b;
            //标记老板想要哪些员工知道消息,老板与这些员工间的连边为花费
            st[i] = a;
        }
        for( int i = 1 ; i <= n ; ++i ){
            if( dis[0][i] == -1 ){
                //对于某些不一定要知道消息的员工,老板与他们间的连边为无穷
                dis[0][i] = dis[i][0] = INF;
            }
        }
        dis[0][0] = 0;
        floyed(n);
        cal(m,n-m);
        printf("%d\n",dp[move(m)-1][0]);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值