修路 BZOJ 4774
修路
【问题描述】
村子间的小路年久失修,为了保障村子之间的往来,法珞决定带领大家修路。对于边带权的无向图 G = (V, E),请选择一些边,使得1 <= i <= d, i号节点和 n - i + 1 号节点可以通过选中的边连通,最小化选中的所有边的权值和。
【输入格式】
第一行两个整数 n, m,表示图的点数和边数。接下来的 m行,每行三个整数 ui, vi, wi,表示有一条 ui 与 vi 之间,权值为 wi 的无向边。
【输出格式】
一行一个整数,表示答案,如果无解输出-1
【样例输入】
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
【样例输出】
8
【数据范围】
1 <= d <= 4
2d <= n <= 10^4
0 <= m <= 10^4
1 <= ui, vi <= n
1 <= wi <= 1000
题解:
考虑到d灰常小,就是斯坦纳树啦
用斯坦纳树求出以u为根,连通情况为opt的最小价值,记为f[u][opt]
由于题目只要 i 与 n - i + 1 连通,那么取出所有对称的opt,取最小值,记为ans[opt]
再暴力Dp合并
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cstdio> 6 #include<cmath> 7 using namespace std; 8 inline void Scan(int &x) 9 { 10 char c; 11 while((c = getchar()) < '0' || c > '9'); 12 x = c - '0'; 13 while((c = getchar()) >= '0' && c <= '9') x = x * 10 + c - '0'; 14 } 15 const int maxn = 100233; 16 const int inf = 707406378; 17 int n, m, d, t, w; 18 int num, all; 19 bool vis[maxn]; 20 int ans[301], que[maxn]; 21 int f[maxn][301]; 22 int tot, nex[maxn], fir[maxn], ver[maxn], pri[maxn]; 23 inline void Ins(int x, int y, int z) 24 { 25 nex[++tot] = fir[x]; 26 fir[x] = tot; 27 ver[tot] = y; 28 pri[tot] = z; 29 } 30 inline void Reset() 31 { 32 memset(f, 127 / 3, sizeof(f)); 33 for(int i = 1; i <= d; ++i) f[i][1 << num++] = 0; 34 for(int i = n - d + 1; i <= n; ++i) f[i][1 << num++] = 0; 35 all = 1 << num; 36 } 37 inline void Spfa(int opt) 38 { 39 t = 0; 40 while(t < w) 41 { 42 int u = que[++t]; 43 for(int i = fir[u]; i; i = nex[i]) 44 { 45 int v = ver[i]; 46 if(f[u][opt] + pri[i] < f[v][opt]) 47 { 48 f[v][opt] = f[u][opt] + pri[i]; 49 if(!vis[v]) 50 { 51 vis[v] = true; 52 que[++w] = v; 53 } 54 } 55 } 56 vis[u] = false; 57 } 58 } 59 int main() 60 { 61 Scan(n), Scan(m), Scan(d); 62 int x, y, z; 63 for(int i = 1; i <= m; ++i) 64 { 65 Scan(x), Scan(y), Scan(z); 66 Ins(x, y, z), Ins(y, x, z); 67 } 68 Reset(); 69 for(int opt = 0; opt < all; ++opt) 70 { 71 w = 0; 72 for(int i = 1; i <= n; ++i) 73 { 74 for(int sub = opt; sub; sub = (sub - 1) & opt) 75 f[i][opt] = min(f[i][opt], f[i][sub] + f[i][opt ^ sub]); 76 if(f[i][opt] != inf) que[++w] = i, vis[i] = true; 77 } 78 Spfa(opt); 79 ans[opt] = inf; 80 } 81 bool flag; 82 for(int opt = 0; opt < all; ++opt) 83 { 84 flag = false; 85 for(int i = 0; i <= d; ++i) 86 { 87 bool a = (opt & (1 << i)) != 0; 88 bool b = (opt & (1 << (d << 1) - i - 1)) != 0; 89 if(a != b) 90 { 91 flag = true; 92 break; 93 } 94 } 95 if(flag) continue; 96 for(int i = 1; i <= n; ++i) ans[opt] = min(ans[opt], f[i][opt]); 97 } 98 for(int opt = 0; opt < all; ++opt) 99 for(int sub = opt; sub; sub = (sub - 1) & opt) 100 ans[opt] = min(ans[opt], ans[sub] + ans[opt ^ sub]); 101 if(ans[all - 1] != inf) printf("%d", ans[all - 1]); 102 else printf("-1"); 103 }