斯坦纳树

Posted huyufeifei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斯坦纳树相关的知识,希望对你有一定的参考价值。

然而就是状压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
", 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 }
AC代码

例题: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("
");
 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 
");
 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 
", 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 
", 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 
", i, f[i][s]);
 99         }
100         SPFA(s);
101         /*for(int i = 0; i < n; i++) {
102             printf("final f %d s = %d 
", 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 
", i, f[i][s]);
107             }*/
108             exmin(g[s], f[i][s]);
109         }
110 //        printf("g = %d 
", 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
", 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 
", T);
137         work();
138         if(T) {
139             clear();
140         }
141     }
142     return 0;
143 }
AC代码

 


以上是关于斯坦纳树的主要内容,如果未能解决你的问题,请参考以下文章

斯坦福nlp:解析树

斯坦纳树

最小斯坦纳树

WC 2008 观光计划(斯坦纳树)

状压dp斯坦纳树

状压dp斯坦纳树