bzoj 4774 修路
Description
村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。对于边带权的无向图 G = (V, E),
请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边
的权值和。
Input
第一行三个整数 n, m,d,表示图的点数和边数。接下来的 m行,每行三个整数 ui, vi, wi,表示有一条 ui 与 vi
之间,权值为 wi 的无向边。
1 <= d <= 4
2d <= n <= 10^4
0 <= m <= 10^4
1 <= ui, vi <= n
1 <= wi <= 1000
Output
一行一个整数,表示答案,如果无解输出-1
Sample Input
10 20 1
6 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7
6 5 1
6 9 4
9 4 2
9 4 10
6 1 2
2 3 6
7 6 10
5 7 1
9 7 2
5 9 10
1 6 8
4 7 4
5 7 1
2 6 9
10 10 6
8 7 2
10 9 10
1 2 4
10 1 8
9 9 7
Sample Output
8
思路: 最小斯坦纳树,先求出f[i][k]表示以i为根的子树那2d个点的状态是k的时候的最小代价。
求出这个f数组以后,我们可以再定义一个g数组,来计算d个点对的情况。
g[1]表示第一个点对联通的情况的最小代价。
g[2]表示第二个点对联通的情况的最小代价。
g[4]表示第三个点对联通的情况的最小代价。
g[3]表示第一个点对和第二个点对都联通的情况下的最小代价。
所以g[k]=min(g[x]+g[k-x])
最后的答案是g[(1<<d)-1]。
1 #include<bits/stdc++.h> 2 using namespace std; 3 int const N=10000+10; 4 int const M=300; 5 int const inf=1e9; 6 struct edge{ 7 int to,nt,w; 8 }e[N<<1]; 9 int f[N][M],cnt,h[N],n,m,d,vis[N],g[M],st[10]; 10 queue<int> q; 11 void add(int a,int b,int c){ 12 e[++cnt].to=b; 13 e[cnt].w=c; 14 e[cnt].nt=h[a]; 15 h[a]=cnt; 16 } 17 int main(){ 18 scanf("%d%d%d",&n,&m,&d); 19 while (m--){ 20 int x,y,z; 21 scanf("%d%d%d",&x,&y,&z); 22 add(x,y,z); 23 add(y,x,z); 24 } 25 for(int i=1;i<=n;i++) 26 for(int j=0;j<(1<<2*d);j++) 27 f[i][j]=inf; 28 for(int i=1;i<=d;i++){ 29 int x=i-1; 30 int y=d+i-1; 31 f[i][1<<x]=0; 32 f[n-i+1][1<<y]=0; 33 st[i]=(1<<x)+(1<<y); 34 } 35 for(int k=1;k<(1<<2*d);k++){ 36 for(int i=1;i<=n;i++) { 37 for(int x=k&(k-1);x;x=(x-1)&k) { 38 int tmp=f[i][x]+f[i][k-x]; 39 f[i][k]=min(f[i][k],tmp); 40 } 41 if(f[i][k]<inf){ 42 q.push(i); vis[i]=1; 43 } 44 } 45 while (!q.empty()){ 46 int x=q.front(); 47 q.pop(); 48 vis[x]=0; 49 for(int i=h[x];i;i=e[i].nt){ 50 int v=e[i].to; 51 if(f[v][k]>f[x][k]+e[i].w){ 52 f[v][k]=f[x][k]+e[i].w; 53 if(!vis[v]){ 54 vis[v]=1; 55 q.push(v); 56 } 57 } 58 } 59 } 60 } 61 for(int k=1;k<(1<<d);k++){ 62 int s=0,t; 63 for(int i=0;i<d;i++) 64 if(k&(1<<i)) s+=st[i+1],t=i+1; 65 g[k]=f[t][s]; 66 for(int x=k&(k-1);x;x=(x-1)&k){ 67 g[k]=min(g[k],g[x]+g[k-x]); 68 } 69 } 70 if(g[(1<<d)-1]==inf) g[(1<<d)-1]=-1; 71 printf("%d\n",g[(1<<d)-1]); 72 return 0; 73 }