斯坦纳树

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

亲测可以用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 }
AC代码

我有一个大胆的想法不知道当不当讲......SPFA,它死了。

posted @ 2019-02-22 17:35  huyufeifei  阅读(...)  评论(...编辑  收藏