斯坦纳树
然而就是状压DP。
具体来说,n个点中有k个关键点,选择一些边把它们连通。求最小边权和。
f[i][s]表示点i与s关键点连通时的最小代价,注意i可以不是关键点。
转移有两种,第一种是i不变,s变。枚举s的子集和补集即可。
第二种是s不变,i变。把第一种转移中的所有非INF的i加入队列跑SPFA。每次从i转移到一个相邻点。
例题:游览计划。这道题没有边权有点权,转移时的代价处理一下即可。
1 #include <cstdio> 2 #include <cstring> 3 #include <queue> 4 5 const int N = 11, M = 101, INF = 0x3f3f3f3f; 6 7 struct Edge { 8 int nex, v; 9 }edge[10010]; int tp; 10 11 struct Pre { 12 int f; // 0 merge 1 spfa 13 int x, y; 14 Pre(int F = -1) { 15 f = F; 16 } 17 }pre[M][1 << 10]; 18 19 std::queue<int> Q; 20 int G[N][N], f[M][1 << 10], n, m, cnt, /*imp[N], */e[M], vis[N]; 21 22 inline void add(int x, int y) { 23 tp++; 24 edge[tp].v = y; 25 edge[tp].nex = e[x]; 26 e[x] = tp; 27 return; 28 } 29 30 inline void exmin(int &a, int b) { 31 a > b ? a = b : 0; 32 return; 33 } 34 35 inline int val(int i) { 36 return G[i / m][i % m]; 37 } 38 39 inline int id(int x, int y) { 40 return x * m + y; 41 } 42 43 inline void SPFA(int s) { 44 while(!Q.empty()) { 45 int x = Q.front(); 46 Q.pop(); // f[x][s] 47 vis[x] = 0; 48 for(int i = e[x]; i; i = edge[i].nex) { 49 int y = edge[i].v; 50 if(f[y][s] > f[x][s] + val(y)) { 51 f[y][s] = f[x][s] + val(y); 52 pre[y][s].f = 1; 53 pre[y][s].x = x; 54 if(!vis[y]) { 55 vis[y] = 1; 56 Q.push(y); 57 } 58 } 59 } 60 } 61 return; 62 } 63 64 void DFS(int i, int s) { 65 if(val(i)) { 66 G[i / m][i % m] = -1; 67 } 68 if(pre[i][s].f == -1) return; 69 if(!pre[i][s].f) { // merge 70 DFS(i, pre[i][s].x); 71 DFS(i, pre[i][s].y); 72 } 73 else { // spfa 74 DFS(pre[i][s].x, s); 75 } 76 return; 77 } 78 79 int main() { 80 memset(f, 0x3f, sizeof(f)); 81 scanf("%d%d", &n, &m); 82 for(int i = 0; i < n; i++) { 83 for(int j = 0; j < m; j++) { 84 scanf("%d", &G[i][j]); 85 if(!G[i][j]) { 86 f[id(i, j)][1 << cnt] = 0; 87 // imp[cnt++] = id(i, j); 88 cnt++; 89 } 90 if(j < m - 1) add(id(i, j), id(i, j + 1)); 91 if(i < n - 1) add(id(i, j), id(i + 1, j)); 92 if(i) add(id(i, j), id(i - 1, j)); 93 if(j) add(id(i, j), id(i, j - 1)); 94 } 95 } 96 97 int lm = 1 << cnt; 98 for(int s = 0; s < lm; s++) { 99 for(int i = 0; i < n * m; i++) { 100 // f[i][s] 101 for(int j = (s - 1) & s; j; j = (j - 1) & s) { 102 int t = s ^ j; 103 // exmin(f[i][s], f[i][t] + f[i][j] - val(i)); 104 if(f[i][s] > f[i][t] + f[i][j] - val(i)) { 105 f[i][s] = f[i][t] + f[i][j] - val(i); 106 pre[i][s].f = 0; 107 pre[i][s].x = t; 108 pre[i][s].y = j; 109 } 110 } 111 if(f[i][s] < INF) { 112 Q.push(i); 113 vis[i] = 1; 114 } 115 } 116 SPFA(s); 117 } 118 119 int ans = INF, p; 120 for(int i = 0; i < n * m; i++) { 121 if(ans > f[i][lm - 1]) { 122 ans = f[i][lm - 1]; 123 p = i; 124 } 125 } 126 127 DFS(p, lm - 1); 128 129 printf("%d\n", ans); 130 for(int i = 0; i < n; i++) { 131 for(int j = 0; j < m; j++) { 132 if(G[i][j] == -1) putchar('o'); 133 else if(G[i][j] == 0) putchar('x'); 134 else putchar('_'); 135 } 136 if(i < n - 1) puts(""); 137 } 138 return 0; 139 }
例题:HDU4085 Peach Blossom Spring 斯坦纳森林(??这是什么词),先求出斯坦纳树然后再来一波DP。
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <queue> 5 6 const int N = 51, M = 1010, INF = 0x3f3f3f3f; 7 8 struct Edge { 9 int nex, v, len; 10 }edge[M << 1]; int tp; 11 12 int f[N][1 << 10], g[1 << 10], valid[1 << 10], cnt[1 << 10]; 13 int e[N], n, m, k, lm; 14 bool vis[N]; 15 std::queue<int> Q; 16 17 inline void add(int x, int y, int z) { 18 tp++; 19 edge[tp].v = y; 20 edge[tp].len = z; 21 edge[tp].nex = e[x]; 22 e[x] = tp; 23 return; 24 } 25 26 inline void exmin(int &a, int b) { 27 a > b ? a = b : 0; 28 return; 29 } 30 31 inline void out(int x) { 32 for(int i = 0; i < k + k; i++) { 33 printf("%d", (x >> i) & 1); 34 } 35 printf("\n"); 36 return; 37 } 38 39 inline void clear() { 40 memset(e, 0, sizeof(e)); 41 memset(valid, 0, sizeof(valid)); 42 tp = 0; 43 return; 44 } 45 46 inline void SPFA(int s) { 47 // printf("SPFA : "); out(s); 48 while(!Q.empty()) { 49 int x = Q.front(); 50 Q.pop(); 51 vis[x] = 0; 52 for(int i = e[x]; i; i = edge[i].nex) { 53 int y = edge[i].v; 54 if(f[y][s] > f[x][s] + edge[i].len) { 55 f[y][s] = f[x][s] + edge[i].len; 56 if(!vis[y]) { 57 vis[y] = 1; 58 Q.push(y); 59 } 60 } 61 } 62 } 63 // printf("SPFA over \n"); 64 return; 65 } 66 67 inline void work() { 68 memset(f, 0x3f, sizeof(f)); 69 memset(g, 0x3f, sizeof(g)); 70 scanf("%d%d%d", &n, &m, &k); 71 lm = 1 << (k << 1); 72 for(int i = 1, x, y, z; i <= m; i++) { 73 scanf("%d%d%d", &x, &y, &z); 74 add(x - 1, y - 1, z); add(y - 1, x - 1, z); 75 } 76 for(int i = 0; i < n; i++) f[i][0] = 0; 77 for(int i = 0; i < k; i++) { 78 f[i][1 << i] = 0; 79 f[n - i - 1][1 << (i + k)] = 0; 80 } 81 for(int s = 0; s < lm; s++) { 82 valid[s] = (cnt[s >> k] == cnt[s & ((1 << k) - 1)]); 83 // printf("s = "); out(s); 84 for(int i = 0; i < n; i++) { 85 // f[i][s] 86 // printf("i = %d \n", i); 87 for(int j = (s - 1) & s; j; j = (j - 1) & s) { 88 int t = s ^ j; 89 /*if(f[i][s] > f[i][t] + f[i][j]) { 90 printf("f = %d \n", f[i][t] + f[i][j]); 91 }*/ 92 exmin(f[i][s], f[i][t] + f[i][j]); 93 } 94 if(f[i][s] < INF) { 95 vis[i] = 1; 96 Q.push(i); 97 } 98 // printf("final f %d s = %d \n", i, f[i][s]); 99 } 100 SPFA(s); 101 /*for(int i = 0; i < n; i++) { 102 printf("final f %d s = %d \n", i, f[i][s]); 103 }*/ 104 for(int i = 0; i < n; i++) { 105 /*if(g[s] > f[i][s]) { 106 printf("i = %d g[s] = %d \n", i, f[i][s]); 107 }*/ 108 exmin(g[s], f[i][s]); 109 } 110 // printf("g = %d \n", g[s]); 111 } 112 // DP 113 for(int s = 0; s < lm; s++) { 114 for(int i = (s - 1) & s; i; i = (i - 1) & s) { 115 if(!valid[i]) continue; 116 exmin(g[s], g[i] + g[s ^ i]); 117 } 118 } 119 if(g[lm - 1] == INF) { 120 puts("No solution"); 121 } 122 else { 123 printf("%d\n", g[lm - 1]); 124 } 125 return; 126 } 127 128 int main() { 129 130 for(int i = 1; i < (1 << 10); i++) { 131 cnt[i] = cnt[(i - (i & (-i))) >> 1] + 1; 132 } 133 int T; 134 scanf("%d", &T); 135 while(T--) { 136 // printf("T = %d \n", T); 137 work(); 138 if(T) { 139 clear(); 140 } 141 } 142 return 0; 143 }
亲测可以用dijkstra代替SPFA,条件是边权非负。本质上是多起点最短路。例题:BZOJ4774
1 #include <bits/stdc++.h> 2 3 const int N = 10040, M = 265, INF = 0x3f3f3f3f; 4 5 struct Edge { 6 int nex, v, len; 7 }edge[N << 1]; int tp; 8 9 int n, e[N], f[N][M], g[M], ok[M]; 10 bool vis[N]; 11 12 inline void add(int x, int y, int z) { 13 edge[++tp].v = y; 14 edge[tp].len = z; 15 edge[tp].nex = e[x]; 16 e[x] = tp; 17 return; 18 } 19 20 struct Node { 21 int x, d; 22 Node(int X = 0, int D = 0) : x(X), d(D) {} 23 inline bool operator < (const Node &w) const { 24 return d > w.d; 25 } 26 }; 27 std::priority_queue<Node> Q; 28 29 inline void dijkstra(int s) { 30 while(Q.size()) { 31 int x = Q.top().x; 32 if(f[x][s] != Q.top().d) { 33 Q.pop(); 34 continue; 35 } 36 Q.pop(); 37 for(int i = e[x]; i; i = edge[i].nex) { 38 int y = edge[i].v; 39 if(f[y][s] > f[x][s] + edge[i].len) { 40 f[y][s] = f[x][s] + edge[i].len; 41 Q.push(Node(y, f[y][s])); 42 } 43 } 44 } 45 return; 46 } 47 48 int main() { 49 int m, d; 50 scanf("%d%d%d", &n, &m, &d); 51 for(int i = 1; i <= m; i++) { 52 int x, y, z; 53 scanf("%d%d%d", &x, &y, &z); 54 if(x == y) continue; 55 add(x, y, z); 56 add(y, x, z); 57 } 58 memset(f, 0x3f, sizeof(f)); 59 for(int i = 1; i <= d; i++) { 60 f[i][1 << (i - 1)] = 0; 61 f[n - i + 1][1 << (i - 1 + d)] = 0; 62 } 63 int lm = (1 << (2 * d)) - 1; 64 for(int s = 1; s <= lm; s++) { 65 for(int i = 1; i <= n; i++) { 66 for(int t = s & (s - 1); t; t = (t - 1) & s) { 67 f[i][s] = std::min(f[i][s], f[i][t] + f[i][s ^ t]); 68 } 69 if(f[i][s] != INF) { 70 Q.push(Node(i, f[i][s])); 71 } 72 } 73 dijkstra(s); 74 } 75 int lm2 = (1 << d) - 1; 76 for(int s = 1; s <= lm2; s++) { 77 /// get sta 78 int sta = 0; 79 for(int i = 1; i <= d; i++) { 80 if((s >> (i - 1)) & 1) { 81 sta |= (1 << (i - 1)) | (1 << (d + i - 1)); 82 } 83 } 84 sta ^= lm; 85 g[s] = INF; 86 for(int i = 1; i <= n; i++) { 87 g[s] = std::min(g[s], f[i][lm]); 88 for(int t = sta; t; t = (t - 1) & sta) { 89 g[s] = std::min(g[s], f[i][lm ^ t]); 90 } 91 } 92 } 93 94 for(int s = 1; s <= lm2; s++) { 95 for(int t = (s - 1) & s; t; t = (t - 1) & s) { 96 g[s] = std::min(g[s], g[t] + g[s ^ t]); 97 } 98 } 99 if(g[lm2] == INF) g[lm2] = -1; 100 printf("%d\n", g[lm2]); 101 return 0; 102 }
我有一个大胆的想法不知道当不当讲......SPFA,它死了。