ZOJ 3613 Wormhole Transport
斯坦纳树,$dp$。
先求出每个状态下连通的最小花费,因为可以是森林,所以$dp$一下。
#include<bits/stdc++.h> using namespace std; int n; const int INF = 0x7FFFFFFF; struct X { int p,s,q; }e[210]; int id[210]; vector<int>g[210]; int val[210][210],d[210][1200],dp[1200]; int f[210*10000],k; queue<int>Q; int num[1200],cost[1200]; void spfa() { while(!Q.empty()) { int h = Q.front(); Q.pop(); f[h]=0; int x=h/10000,y=h%10000; for(int i=0;i<g[x].size();i++) { int to = g[x][i]; if(to<k) { if(((1<<to)&y)==0) { if(d[x][y]+val[x][to]<d[to][y|(1<<to)]) d[to][y|(1<<to)]=d[x][y]+val[x][to]; } } else { if(d[x][y]+val[x][to]<d[to][y]) { d[to][y] = d[x][y]+val[x][to]; if(f[to*10000+y]==0) { f[to*10000+y]=1; Q.push(to*10000+y); } } } } } } bool cmp(X a,X b) { if(a.p!=b.p) return a.p>b.p; return a.s>b.s; } void work(int x) { int A=0,B=0; for(int i=0;i<k;i++) { if(((1<<i)&x)==0) continue; A=A+e[i].p; B=B+e[i].s; } num[x] = min(A,B); cost[x] = dp[x]; } int main() { while(~scanf("%d",&n)) { for(int i=0;i<n;i++) { scanf("%d%d",&e[i].p,&e[i].s); e[i].p = min(4,e[i].p); e[i].q = i; } sort(e,e+n,cmp); for(int i=0;i<n;i++) id[e[i].q]=i; for(int i=0;i<n;i++) for(int j=0;j<n;j++) val[i][j]=INF; memset(f,0,sizeof f); for(int i=0;i<n;i++) g[i].clear(); int m; scanf("%d",&m); for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); a--; b--; a=id[a]; b=id[b]; val[a][b]=min(val[a][b],c); val[b][a]=val[a][b]; g[a].push_back(b); g[b].push_back(a); } k=0; for(int i=0;i<n;i++) if(e[i].p||e[i].s) k++; int st = 1<<k; for(int j=0;j<st;j++) for(int i=0;i<n;i++) d[i][j]=INF; for(int i=0;i<n;i++) { if(i<k) d[i][1<<i]=0; else d[i][0]=0; } for(int j=0;j<st;j++) { for(int i=0;i<n;i++) { if(i<k) { if(((1<<i)&j)==0) continue; for (int x = j; x; x = (x-1)&j) { int A=x ,B=j-A; if(d[i][A|(1<<i)]!=INF&&d[i][B|(1<<i)]!=INF) d[i][j] = min(d[i][j], d[i][A|(1<<i)]+d[i][B|(1<<i)]); } } else { for (int x = j; x; x = (x-1)&j) { int A=x ,B=j-A; if(d[i][A]!=INF&&d[i][B]!=INF) d[i][j] = min(d[i][j], d[i][A]+d[i][B]); } } if(d[i][j]!=INF) Q.push(i*10000+j); } spfa(); } for(int j=0;j<st;j++) { dp[j]=INF; for(int i=0;i<n;i++) dp[j]=min(dp[j],d[i][j]); } memset(num,0,sizeof num); memset(cost,0,sizeof cost); for(int i=0;i<st;i++) { if(dp[i]!=INF) work(i); for (int x = i; x; x = (x-1)&i) { int A=x, B=i-x; if(num[A]+num[B]>num[i]) { num[i] = num[A]+num[B]; cost[i] = cost[A]+cost[B]; } else if(num[A]+num[B]==num[i]) { if(cost[A]+cost[B]<cost[i]) cost[i] = cost[A]+cost[B]; } } } int ans1=0,ans2=999999999; for(int i=0;i<st;i++) ans1=max(ans1,num[i]); for(int i=0;i<st;i++) { if(num[i]!=ans1) continue; ans2=min(ans2,cost[i]); } printf("%d %d\n",ans1,ans2); } return 0; }