NOI 2009 植物大战僵尸 | 最大权闭合子图

Posted milky-w

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOI 2009 植物大战僵尸 | 最大权闭合子图相关的知识,希望对你有一定的参考价值。

luogu 2805

最大权闭合子图:

  • 选择一些点,并且这些点出边所指向的点也必须被选择,一些点有收益,一些点有代价,最大化所选权值的收益-代价。

方法:

  • 源点S向所有有收益的点连流量为收益的边
  • 有代价的点向汇点T连流量为代价的边
  • 所有点向所有必须选择的点连流量为inf的边
  • 答案是所有收益的和减去最小割

证明:

  • 统计出收益和,割边时选择收益和代价中较小的减去,若收益小于代价,就放弃这个收益,若收益大于代价,就减去代价。

该题会出现环,环上的点不能选,逆拓扑排序去除。

  1 #include <cstdio>
  2 #include <string>
  3 #include <vector>
  4 #include <queue>
  5 
  6 const int N = 1000, INF = 0x3f3f3f3f;
  7 
  8 int read() {
  9     int x = 0, f = 1;
 10     char c = getchar();
 11     while (!isdigit(c)) {
 12         if (c == -) f = -1;
 13         c = getchar();
 14     }
 15     while (isdigit(c)) {
 16         x = (x << 3) + (x << 1) + (c ^ 48);
 17         c = getchar();
 18     }
 19     return x * f;
 20 }
 21 
 22 int min(int x, int y) {
 23     if (x <= y) return x; return y;
 24 }
 25 
 26 struct Edge {
 27     int from, to, flow, cap;
 28 };
 29 std::vector<Edge> edges;
 30 std::vector<int> G[N];
 31 
 32 int p[N], d[N], S, T, n, outdgr[N], w[N];
 33 
 34 void addEdge(int from, int to, int cap) {
 35     edges.push_back((Edge){from, to, 0, cap});
 36     edges.push_back((Edge){to, from, 0, 0});
 37     int size = edges.size();
 38     G[from].push_back(size - 2);
 39     G[to].push_back(size - 1);
 40 }
 41 
 42 void bfs() {
 43     bool vis[N] = {};
 44     std::queue<int> Q; Q.push(T);
 45     for (int i = 0; i < n; ++ i) d[i] = n;
 46     d[T] = 0, vis[T] = 1;
 47     while (!Q.empty()) {
 48         int u = Q.front(); Q.pop();
 49         int size = G[u].size();
 50         for (int i = 0; i < size; ++ i) {
 51             Edge &e = edges[G[u][i]], &g = edges[G[u][i]^1];
 52             if (!vis[e.to] && g.cap > g.flow) {
 53                 d[e.to] = d[u] + 1;
 54                 vis[e.to] = 1;
 55                 Q.push(e.to);
 56             }
 57         }
 58     }
 59 }
 60 
 61 int augment() {
 62     int x = T, a = INF;
 63     while (x != S) {
 64         a = min(a, edges[p[x]].cap - edges[p[x]].flow);
 65         x = edges[p[x]].from;
 66     }
 67     x = T;
 68     while (x != S) {
 69         edges[p[x]].flow += a, edges[p[x]^1].flow -= a;
 70         x = edges[p[x]].from;
 71     }
 72     return a;
 73 }
 74 
 75 int maxflow() {
 76     int num[N] = {}, cur[N] = {};
 77     bfs();
 78     for (int i = 0; i < n; ++ i) ++ num[d[i]];
 79     int x = S, flow = 0;
 80     while (d[S] < n) {
 81         if (x == T) flow += augment(), x = S;
 82         int size = G[x].size(); bool ok = false;
 83         for (int i = cur[x]; i < size; ++ i) {
 84             Edge &e = edges[G[x][i]];
 85             if (e.cap > e.flow && d[e.to] + 1 == d[x]) {
 86                 p[e.to] = G[x][i], cur[x] = i, ok = true, x = e.to;
 87                 break;
 88             }
 89         }
 90         if (!ok) {
 91             int size = G[x].size(), m = n - 1;
 92             for (int i = 0; i < size; ++ i) {
 93                 Edge &e = edges[G[x][i]];
 94                 if (e.cap > e.flow) m = min(m, d[e.to]);
 95             }
 96             if (--num[d[x]] == 0) break;
 97             ++num[d[x] = m + 1], cur[x] = 0;
 98             if (x != S) x = edges[p[x]].from;
 99         }
100     }
101     return flow;
102 }
103 
104 int main() {
105     n = read(); int m = read(), sum = 0; S = 0, T = n * m + 1;
106     for (int i = 1; i <= n * m; ++ i) {
107         w[i] = read(); int k = read();
108         if (i % m) {
109             addEdge(i, i + 1, INF);
110             ++outdgr[i];
111         }
112         for (int j = 1; j <= k; ++ j) {
113             int x = read(), y = read();
114             addEdge(x * m + y + 1, i, INF);
115             ++outdgr[x * m + y + 1];
116         }
117     }
118     std::queue<int> Q;
119     for (int i = 1; i <= n * m; ++ i) {
120         if (outdgr[i] == 0) Q.push(i);
121     }
122     while (!Q.empty()) {
123         int u = Q.front(); Q.pop();
124         int size = G[u].size();
125         for (int i = 0; i < size; ++ i) {
126             int v = edges[G[u][i]].to;
127             if (outdgr[v] && --outdgr[v] == 0) Q.push(v);
128         }
129     }
130     for (int i = 1; i <= n * m; ++ i) {
131         if (outdgr[i] == 0) {
132             if (w[i] >= 0) sum += w[i], addEdge(S, i, w[i]);
133             else addEdge(i, T, -w[i]);
134         }
135     } n = n * m + 2;
136     printf("%d\n", sum - maxflow());
137     return 0;
138 }

 

以上是关于NOI 2009 植物大战僵尸 | 最大权闭合子图的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 1565 [NOI2009]植物大战僵尸tarjan+最大权闭合子图

BZOJ1565 NOI 2009 植物大战僵尸 topo+最小割(最大权闭合子图)

bzoj 1565 [NOI2009]植物大战僵尸

luogu2805 [NOI2009]植物大战僵尸

[NOI2009]植物大战僵尸

[Luogu 2805]NOI2009 植物大战僵尸