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

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 }      
View Code

 

posted @ 2019-08-15 16:54  zjxxcn  阅读(148)  评论(0编辑  收藏  举报