[NOI2009]植物大战僵尸

题目:洛谷P2805、BZOJ1565。

题目大意:

在n×m的网格上,每个格子有植物,吃了每个植物能获得一些价值(可能为负),每个植物可能会保护另一些植物,要吃被保护的植物必须先消灭该植物。你可以在最右边放僵尸。问最大价值。

解题思路:

PVZ真好玩

首先,右边的植物也是保护左边的植物的。

然后,就是最大权闭合子图模型。最大流即可。

从S向价值为正的植物连容量为价值的边;从价值为负的植物向T连容量为价值的相反数的边。

从被保护的植物向保护的植物连容量inf的边。

答案即为价值为正的植物的价值总和-最大流。

然鹅发现并过不去。因为有环!植物和植物可能存在着循环保护,然后这些植物就无敌了(无敌植物所保护的其他植物也无敌)。

故我们首先要用Tarjan找强连通分量,把环找出来,然后删掉这些无敌的点(包括其保护的点等)。

接着再建图跑网络流即可。

C++ Code:

#include<bits/stdc++.h>
const int S=0,T=6665,inf=0x3fffffff;
inline int readint(){
    int c=getchar(),d=0,f=0;
    for(;!isdigit(c);c=getchar())f=c=='-';
    for(;isdigit(c);c=getchar())
    d=(d<<3)+(d<<1)+(c^'0');
    return f?-d:d;
}
int n,m,sco[666],low[666],dfn[666],idx=0,vis[666],head[6666],iter[6666],level[6666],cnt=1;
bool ok[666];
inline int num(int a,int b){return(a-1)*m+b;}
inline int min(int a,int b){return a<b?a:b;}
std::vector<int>v[666];
std::stack<int>s;
void tarjan(int now){
    low[now]=dfn[now]=++idx;
    s.push(now);vis[now]=1;
    for(int i=v[now].size()-1;~i;--i)
    if(!dfn[v[now][i]]){
        tarjan(v[now][i]);
        low[now]=min(low[now],low[v[now][i]]);
    }else
    if(vis[v[now][i]])low[now]=min(low[now],dfn[v[now][i]]);
    if(low[now]==dfn[now]){
        int sz=1;
        for(;s.top()!=now;++sz){
            ok[s.top()]=false;
            vis[s.top()]=0;
            s.pop();
        }
        vis[now]=0;
        s.pop();
        if(sz>1)ok[now]=false;
    }
}
struct edge{
    int to,nxt,cap;
}e[2333333];
inline void addedge(int from,int to,int flow){
    e[++cnt]=(edge){to,head[from],flow};
    head[from]=cnt;
    e[++cnt]=(edge){from,head[to],0};
    head[to]=cnt;
}
std::queue<int>q;
void bfs(){
    level[S]=1;
    for(q.push(S);!q.empty();){
        int u=q.front();
        q.pop();
        for(int i=head[u];~i;i=e[i].nxt)
        if(e[i].cap&&!~level[e[i].to]){
            level[e[i].to]=level[u]+1;
            q.push(e[i].to);
        }
    }
}
int dfs(int u,int f){
    if(!f||u==T)return f;
    for(int& i=iter[u];~i;i=e[i].nxt)
    if(e[i].cap&&level[e[i].to]>level[u]){
        int d=dfs(e[i].to,min(f,e[i].cap));
        if(d){
            e[i].cap-=d;
            e[i^1].cap+=d;
            return d;
        }else level[e[i].to]=-1;
    }
    return 0;
}
int dinic(){
    for(int flow=0,f;;){
        memset(level,-1,sizeof iter);
        if(bfs(),!~level[T])return flow;
        memcpy(iter,head,sizeof iter);
        while(f=dfs(S,inf))flow+=f;
    }
}
void color(int now){
    for(int i=v[now].size()-1;~i;--i)
    if(ok[v[now][i]]){
        ok[v[now][i]]=false;
        color(v[now][i]);
    }
}
int main(){
    n=readint(),m=readint();
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j){
        int id=num(i,j);
        sco[id]=readint();
        for(int cc=readint();cc--;){
            int x=readint()+1,y=readint()+1;
            v[id].push_back(num(x,y));
        }
        if(j>1)v[id].push_back(id-1);
    }
    memset(dfn,0,sizeof dfn);
    memset(vis,0,sizeof vis);
    memset(head,-1,sizeof head);
    memset(ok,1,sizeof ok);
    for(int i=1;i<=n*m;++i)
    if(!dfn[i])tarjan(i);
    int sm=0;
    for(int i=1;i<=n*m;++i)
    if(!ok[i])color(i);
    for(int i=1;i<=n*m;++i)
    if(ok[i]){
        if(sco[i]>0)addedge(S,i,sco[i]);else
        addedge(i,T,-sco[i]);
        sm+=(sco[i]>0)*sco[i];
        for(int j=v[i].size()-1;~j;--j)
        if(ok[v[i][j]])addedge(v[i][j],i,inf);
    }
    printf("%d
",sm-dinic());
    return 0;
}
原文地址:https://www.cnblogs.com/Mrsrz/p/9307462.html