P2805 [NOI2009]植物大战僵尸(最小割+拓扑排序)
题意:
n*m的矩阵,每个位置都有一个植物。每个植物都有一个价值(可以为负),以及一些它可以攻击的位置。从每行的最右面开始放置僵尸,僵尸从右往左行动,当僵尸在植物攻击范围内时会立刻死亡。僵尸每到一个位置可以获得该位置植物的价值。僵尸可以无限放置,求最大的价值和。
题解:
这种模型好像叫做最大权闭合子图。
首先通过拓扑排序将成环的点(即植物互相保护无法走到的点)删掉。
之后对于剩下的点A,若w > 0,则S→A连权值为w的边。
若w < 0,则A→T连权值为-w的边。
若A保护B(即B在A攻击范围内),则B→A连权值为INF的边。
注意每个点也被它右面的点保护。
最后跑一边最小割,答案为:正的价值和(即w > 0的和) - 最小割。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> using namespace std; const int N = 605; const int INF = 2e9; int n, m; int level[N]; int iter[N]; int x, y; int w[N], d[N]; vector<int> g[N]; vector<int> a; struct edge { int to, cap, rev; }; vector<edge> G[N]; void add_edge(int from, int to, int cap) { G[from].push_back((edge){to, cap, G[to].size()}); G[to].push_back((edge){from, 0, G[from].size()-1}); } void bfs(int s) { memset(level, -1, sizeof(level)); queue<int> que; level[s] = 0; que.push(s); while(!que.empty()) { int v = que.front(); que.pop(); int len = G[v].size(); for(int i = 0; i < len; i++) { edge &e = G[v][i]; if(e.cap > 0 && level[e.to] < 0) { level[e.to] = level[v]+1; que.push(e.to); } } } } int dfs(int v, int t, int f) { if(v == t) return f; int len = G[v].size(); for(int &i = iter[v]; i < len; i++) { edge &e = G[v][i]; if(e.cap > 0 && level[v] < level[e.to]) { int d = dfs(e.to, t, min(f, e.cap)); if(d > 0) { e.cap -= d; G[e.to][e.rev].cap += d; return d; } } } return 0; } int max_flow(int s, int t) { int flow = 0; for(;;) { bfs(s); if(level[t] < 0) return flow; memset(iter, 0, sizeof(iter)); int f; while((f = dfs(s, t, INF)) > 0) flow += f; } } int main() { scanf("%d%d", &n, &m); for(int i = 1; i <= n*m; i++) { int num; scanf("%d%d", &w[i], &num); while(num--) { scanf("%d%d", &x, &y); int id = x*m+y+1; g[i].push_back(id); d[id]++; } if(i % m != 1) g[i].push_back(i-1), d[i-1]++; } queue<int> q; for(int i = 1; i <= n*m; i++) if(!d[i]) q.push(i); while(!q.empty()) { int v = q.front(); q.pop(); a.push_back(v); int len = g[v].size(); for(int i = 0; i < len; i++) { int to = g[v][i]; d[to]--; if(!d[to]) q.push(to); } } int sum = 0; int len = a.size(); for(int i = 0; i < len; i++) { int v = a[i]; if(w[v] >= 0) { sum += w[v]; add_edge(0, v, w[v]); } else add_edge(v, n*m+1, -w[v]); int up = g[v].size(); for(int j = 0; j < up; j++) add_edge(g[v][j], v, INF); } printf("%d\n", sum-max_flow(0, n*m+1)); }