题目链接:传送门
1965: Message
Time Limit: 1 Sec Memory Limit: 128 Mb Submitted: 68 Solved: 7Description
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 ≤ 1000, Cii = 0, Cij = 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;
}