BZOJ1565 [NOI2009]植物大战僵尸 【最大权闭合子图 + tarjan缩点(或拓扑)】

题目

这里写图片描述

输入格式

这里写图片描述

输出格式

仅包含一个整数,表示可以获得的最大能源收入。注意,你也可以选择不进行任何攻击,这样能源收入为0。

输入样例

3 2

10 0

20 0

-10 0

-5 1 0 0

100 1 2 1

100 0

输出样例

25

提示

在样例中, 植物P1,1可以攻击位置(0,0), P2, 0可以攻击位置(2,1)。
一个方案为,首先进攻P1,1, P0,1,此时可以攻击P0,0 。共得到能源收益为(-5)+20+10 = 25。注意, 位置(2,1)被植物P2,0保护,所以无法攻击第2行中的任何植物。
【大致数据规模】
约20%的数据满足1 ≤ N, M ≤ 5;
约40%的数据满足1 ≤ N, M ≤ 10;
约100%的数据满足1 ≤ N ≤ 20,1 ≤ M ≤ 30,-10000 ≤ Score ≤ 10000 。

题解

僵尸想吃一个植物,首先得吃掉它前面的植物和保护它的植物
这就是有附属关系的最大权选择问题,即最大权闭合子图

但是要注意图中的环是无敌的,我们要忽略掉这些点
可以用tarjan或拓扑找环
还没完
被环保护的点也是无敌的,所以还要从环出发排除掉被环保护的点【一开始没注意到这个WA了2发】

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)
using namespace std;
const int maxn = 1005,maxm = 2000005,INF = 1000000000;
inline int RD(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
    return out * flag;
}
int s[maxn],id[22][32],N,M,cnt = 0,S,T,f[maxn];
int h[maxn],ne = 0;
struct EDGE{int to,nxt,f;}ed[maxm];
void build(int u,int v,int f){
    ed[ne] = (EDGE){v,h[u],f}; h[u] = ne++;
    ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;
}
int dfn[maxn],low[maxn],Scc[maxn],scci = 0,Siz[maxn],st[maxn],top = 0,Cnt = 0;
void dfs(int u){
    low[u] = dfn[u] = ++Cnt;
    st[++top] = u; int to;
    Redge(u){
        if (k & 1) continue;
        if (!dfn[to = ed[k].to]) dfs(to);
        if (!Scc[to]) low[u] = min(low[u],low[to]);
    }
    if (dfn[u] == low[u]){
        scci++;
        do {Scc[st[top]] = scci; Siz[scci]++;}while (st[top--] != u);
    }
}
void tarjan(){
    REP(i,cnt) if (!dfn[i]) dfs(i);
}
int d[maxn],vis[maxn],cur[maxn];
bool bfs(){
    queue<int> q; int u,to;
    for (int i = S; i <= T; i++) d[i] = INF,vis[i] = false;
    q.push(S); vis[S] = true; d[S] = 0;
    while (!q.empty()){
        u = q.front(); q.pop();
        Redge(u) if (ed[k].f && !f[to = ed[k].to] && !vis[to]){
            d[to] = d[u] + 1; vis[to] = true; q.push(to);
        }
    }
    return vis[T];
}
int dfs(int u,int minf){
    if (u == T || !minf) return minf;
    int flow = 0,f,to;
    if (cur[u] == -2) cur[u] = h[u];
    for (int& k = cur[u]; k != -1; k = ed[k].nxt)
        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(ed[k].f,minf)))){
            ed[k].f -= f; ed[k ^ 1].f += f;
            flow += f; minf -= f;
            if (!minf) break;
        }
    return flow;
}
int maxflow(){
    int flow = 0;
    while (bfs()){
        fill(cur,cur + maxn,-2);
        flow += dfs(S,INF);
    }
    return flow;
}
void dfs1(int u){
    f[u] = true;
    Redge(u) if ((k & 1) && !f[ed[k].to]) dfs1(ed[k].to);
}
int main(){
    memset(h,-1,sizeof(h));
    N = RD(); M = RD(); int t,x,y,tot = 0;
    REP(i,N) REP(j,M) id[i - 1][j - 1] = ++cnt;
    for (int i = 0; i < N; i++)
        for (int j = 0; j < M; j++)
            if (j + 1 < M)
                build(id[i][j],id[i][j + 1],INF);
    S = 0; T = cnt + 1;
    for (int i = 1; i <= cnt; i++){
        s[i] = RD(); t = RD();
        while (t--) x = RD(),y = RD(),build(id[x][y],i,INF);
    }
    tarjan();
    REP(i,cnt) if (Siz[Scc[i]] > 1) dfs1(i);
    REP(i,cnt) if (!f[i]){
        if (s[i] >= 0) build(S,i,s[i]),tot += s[i];
        else build(i,T,-s[i]);
    }
    printf("%d",tot - maxflow());
    return 0;
}
posted @ 2018-01-03 14:26  Mychael  阅读(216)  评论(0编辑  收藏  举报