【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=4006
【题意】
给定n点m边的图,连接边(u,v)需要花费w,问将k个点中同颜色的点连接的最小费用。
【思路】
题目所求斯坦纳森林。
如果我们知道满足颜色集合S的最小值g[S],则有递推公式:
g[S]=min{ g[S’]+g[S-S’] }
则g[(1<<C)-1]即答案。
这里的g[S],其实就是一颗包含S中所有颜色的斯坦纳树,即求一棵包含所有颜色在S中的点的斯坦纳树,我们设f[i][st]为在i点且包含点集为st的最小花费则有转移式:
f[i][st]=min{ f[i][st’]+f[i][st-st’] }
f[i][st]=min{ f[i’][st]+weight(i,i’) }
两次状压DP bingo。memset那里可以优化一下,懒得改了 =_=
【代码】
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 using namespace std; 12 13 typedef long long ll; 14 const int N = 1e3+10; 15 const int M = 4e3+10; 16 const int P = 12; 17 const int inf = 0xf0f0f0f; 18 19 ll read() { 20 char c=getchar(); 21 ll f=1,x=0; 22 while(!isdigit(c)) { 23 if(c=='-') f=-1; c=getchar(); 24 } 25 while(isdigit(c)) 26 x=x*10+c-'0',c=getchar(); 27 return x*f; 28 } 29 struct Edge { int v,w,nxt; 30 }e[M<<1]; 31 int en=1,front[N]; 32 void adde(int u,int v,int w) 33 { 34 e[++en]=(Edge){v,w,front[u]}; front[u]=en; 35 } 36 struct Node { 37 int c,w; 38 bool operator < (const Node& rhs) const { 39 return c<rhs.c; 40 } 41 }ns[P]; 42 43 int n,m,K,cnt=0; 44 int f[N][1<<P],g[1<<P]; 45 46 queue<int> q; int inq[N]; 47 48 void spfa(int sta) 49 { 50 while(!q.empty()) { 51 int u=q.front(); q.pop(); inq[u]=0; 52 trav(u,i) { 53 int v=e[i].v; 54 if(f[v][sta]>f[u][sta]+e[i].w) { 55 f[v][sta]=f[u][sta]+e[i].w; 56 if(!inq[v]) 57 inq[v]=1,q.push(v); 58 } 59 } 60 } 61 } 62 int solve() 63 { 64 int all=1<<cnt; 65 FOR(sta,1,all-1) { 66 FOR(i,1,n) { 67 for(int s=(sta-1)&sta;s;s=(s-1)&sta) 68 f[i][sta]=min(f[i][sta],f[i][s]+f[i][sta-s]); 69 if(f[i][sta]!=inf) q.push(i),inq[i]=1; 70 } 71 spfa(sta); 72 } 73 int ans=inf; 74 FOR(i,1,n) ans=min(ans,f[i][all-1]); 75 return ans; 76 } 77 78 int main() 79 { 80 freopen("channel.in","r",stdin); 81 freopen("channel.out","w",stdout); 82 n=read(),m=read(),K=read(); 83 FOR(i,1,m) { 84 int u=read(),v=read(),w=read(); 85 adde(u,v,w); adde(v,u,w); 86 } 87 FOR(i,1,K) { 88 ns[i].c=read(),ns[i].w=read(); 89 } 90 sort(ns+1,ns+K+1); 91 int C=0; 92 FOR(i,1,K) { 93 if(ns[i].c!=ns[i-1].c) C++; 94 ns[i].c=C; 95 } 96 memset(g,0xf,sizeof(g)); 97 int all=1<<C; 98 FOR(sta,1,all-1) { 99 memset(f,0xf,sizeof(f)); 100 cnt=0; FOR(i,1,K) if((1<<ns[i].c-1)&sta) f[ns[i].w][1<<cnt++]=0; 101 g[sta]=solve(); 102 } 103 FOR(sta,1,all-1) { 104 for(int s=(sta-1)&sta;s;s=(s-1)&sta) 105 g[sta]=min(g[sta],g[s]+g[sta-s]); 106 } 107 printf("%d\n",g[all-1]); 108 return 0; 109 }