P2805/BZOJ1565 [NOI2009]植物大战僵尸

每个植物向它能保护的植物连边,矩阵中每个点向它右边相邻的点连边。

用拓扑排序去除相互保护的植物所成的环,发现将剩余代表保护的边倒置后,答案即为最大权闭合子图。

代码如下。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e4+10,M=1e6+10,inf=0x3f3f3f3f;
int n,m,s,t,ans,tot=1,a[N],d[N],v[N],deg[N],head[N],ver[M],next[M],edge[M];vector<int> g[N];queue<int> q;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
inline void add(int x,int y,int z){
    ver[++tot]=y;edge[tot]=z;next[tot]=head[x];head[x]=tot;
    ver[++tot]=x;edge[tot]=0;next[tot]=head[y];head[y]=tot;
}
inline bool bfs(){
    memset(d,0,sizeof(d));
    queue<int> q;d[s]=1;q.push(s);
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=head[x];i;i=next[i]){
            int y=ver[i],z=edge[i];
            if(d[y]||!z) continue;
            d[y]=d[x]+1;q.push(y);
            if(y==t) return 1;
        }
    }
    return 0;
}
inline int dinic(int x,int flow){
    if(x==t) return flow;
    int rest=flow;
    for(int i=head[x];i&&rest;i=next[i]){
        int y=ver[i],z=edge[i];
        if(d[y]!=d[x]+1||!z) continue;
        int k=dinic(y,min(z,rest));
        if(!k) d[y]=0;
        else edge[i]-=k,edge[i^1]+=k,rest-=k;
    }
    return flow-rest;
}
int main(){
    n=read();m=read();s=n*m;t=s+1;
    for(int i=0;i<s;i++){
        a[i]=read();int p=read();
        while(p--){
            int x=read(),y=read();
            g[i].push_back(x*m+y);deg[x*m+y]++;
        }
    } 
    for(int i=0;i<n;i++){
        for(int j=1;j<m;j++){
            g[i*m+j].push_back(i*m+j-1);deg[i*m+j-1]++;
        }
    }
    for(int i=0;i<s;i++) if(!deg[i]) q.push(i),v[i]=1;
    while(q.size()){
        int x=q.front();q.pop();
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i];
            if(!v[y]&&!--deg[y]) q.push(y),v[y]=1; 
        }
    }
    for(int x=0;x<s;x++){
        if(!v[x]) continue;
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i];if(!v[y]) continue;
            add(y,x,inf);
        }
        if(a[x]>0) add(s,x,a[x]),ans+=a[x];
        else if(a[x]<0) add(x,t,-a[x]);
    }
    while(bfs()) ans-=dinic(s,inf);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-05-11 10:42  小塘一点空明  阅读(146)  评论(0编辑  收藏  举报