P2805 [NOI2009]植物大战僵尸(最小割+拓扑排序)

题意:

  n*m的矩阵,每个位置都有一个植物。每个植物都有一个价值(可以为负),以及一些它可以攻击的位置。从每行的最右面开始放置僵尸,僵尸从右往左行动,当僵尸在植物攻击范围内时会立刻死亡。僵尸每到一个位置可以获得该位置植物的价值。僵尸可以无限放置,求最大的价值和。

题解:

  这种模型好像叫做最大权闭合子图。

  首先通过拓扑排序将成环的点(即植物互相保护无法走到的点)删掉。

  之后对于剩下的点A,若w > 0,则S→A连权值为w的边。

  若w < 0,则A→T连权值为-w的边。

  若A保护B(即B在A攻击范围内),则B→A连权值为INF的边。

  注意每个点也被它右面的点保护。

  最后跑一边最小割,答案为:正的价值和(即w > 0的和) - 最小割。

#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));
}
View Code

猜你喜欢

转载自www.cnblogs.com/Pneuis/p/9858758.html