【BZOJ4774】修路 [斯坦纳树]
修路
Time Limit: 20 Sec Memory Limit: 256 MBDescription
Input
Output
仅一行一个整数表示答案。
Sample Input
5 5 2
1 3 4
3 5 2
2 3 1
3 4 4
2 4 3
Sample Output
9
HINT
Main idea
给定若干对点,选择若干边,询问满足每对点都连通的最小代价。
Solution
发现 d 非常小,所以我们显然可以使用斯坦纳树来求解。
斯坦纳树是用来解决这种问题的:给定若干关键点,求使得关键点连通的最小代价。
我们可以令 f[i][opt] 表示以 i 为根时,关键点连通态为opt的最小代价。(以二进制表示是否连通)
然后我们就可以用两种方法来更新 f[i][opt]:
1. 设定集合x,y,x∪y=opt且x∩y=∅,那么我们显然就可以将用x,y合并来更新opt,
2. 若 f[j][opt] 中opt = f[i][opt]中opt,那么我们就可以以连边方式合并两个集合,这种合并方式显然可以用最短路实现,使得答案更优。
然后我们就可以求出所有状态的f[i][opt],接下来再利用DP,求解。
定义Ans[opt]表示连通态为opt时最小代价,如果对应点同时连通或不连通则可以更新,枚举所有情况就可以求出答案了。
Code
1 #include<iostream>
2 #include<algorithm>
3 #include<cstdio>
4 #include<cstring>
5 #include<cstdlib>
6 #include<cmath>
7 using namespace std;
8
9 const int ONE = 20005;
10 const int MOD = 1e9+7;
11
12 int n,m,d;
13 int x,y,z;
14 int a[ONE];
15 int next[ONE],first[ONE],go[ONE],w[ONE],tot;
16 int All,f[ONE/2][258],INF;
17 int q[10000005],vis[ONE],tou,wei;
18 int Ans[258];
19
20 int get()
21 {
22 int res=1,Q=1; char c;
23 while( (c=getchar())<48 || c>57)
24 if(c=='-')Q=-1;
25 if(Q) res=c-48;
26 while((c=getchar())>=48 && c<=57)
27 res=res*10+c-48;
28 return res*Q;
29 }
30
31 void Add(int u,int v,int z)
32 {
33 next[++tot]=first[u]; first[u]=tot; go[tot]=v; w[tot]=z;
34 next[++tot]=first[v]; first[v]=tot; go[tot]=u; w[tot]=z;
35 }
36
37
38 namespace Steiner
39 {
40 void pre()
41 {
42 memset(f,63,sizeof(f)); INF=f[0][0];
43 int num = 0;
44 for(int i=1;i<=d;i++) f[i][1<<num] = 0, num++;
45 for(int i=n-d+1;i<=n;i++) f[i][1<<num] = 0, num++;
46 All = (1<<num) - 1;
47 }
48
49 void Spfa(int opt)
50 {
51 while(tou<wei)
52 {
53 int u=q[++tou];
54 for(int e=first[u];e;e=next[e])
55 {
56 int v=go[e];
57 if(f[v][opt] > f[u][opt] + w[e])
58 {
59 f[v][opt] = f[u][opt] + w[e];
60 if(!vis[v])
61 {
62 vis[v] = 1;
63 q[++wei] = v;
64 }
65 }
66 }
67 vis[u] = 0;
68 }
69 }
70
71 void Deal()
72 {
73 for(int opt=0;opt<=All;opt++)
74 {
75 for(int i=1;i<=n;i++)
76 {
77 for(int sub=opt;sub;sub=(sub-1) & opt)
78 f[i][opt] = min(f[i][opt],f[i][sub]+f[i][opt^sub]);
79 if(f[i][opt] != INF)
80 {
81 q[++wei] = i;
82 vis[i] = 1;
83 }
84 }
85 Spfa(opt);
86 }
87 }
88 }
89
90 bool Check(int opt)
91 {
92 for(int i=0,j=(d<<1)-1; i<d; i++,j--)
93 if( ((opt & (1<<i))== 0) != ((opt & (1<<j))==0) )
94 return 0;
95 return 1;
96 }
97
98 int main()
99 {
100 n=get(); m=get(); d=get();
101 for(int i=1;i<=m;i++)
102 {
103 x=get(); y=get(); z=get();
104 Add(x,y,z);
105 }
106
107 Steiner::pre();
108 Steiner::Deal();
109
110 memset(Ans,63,sizeof(Ans));
111 for(int opt=0;opt<=All;opt++)
112 if(Check(opt))
113 {
114 for(int i=1;i<=n;i++)
115 Ans[opt] = min(Ans[opt], f[i][opt]);
116 }
117
118 for(int opt=0;opt<=All;opt++)
119 for(int sub=opt;sub;sub=(sub-1) & opt)
120 Ans[opt] = min(Ans[opt], Ans[sub]+Ans[opt^sub]);
121
122 if(Ans[All] == INF) printf("-1");
123 else printf("%d",Ans[All]);
124 }