题解 [NOI2009] 植物大战僵尸

  讲网络流的时候没有听懂最大权闭合子图,然后讲题的时候学长问我们这是什么模型。

  我就瞎口胡了最大权闭合子图,然后就中了。

  总之,依题可知,要吃一个植物,必须吃它右侧的植物,并且吃掉保护它的植物(全都是玉米加农炮嗷)。所以在两个点之间连一条有向边,连完之后发现要获取一个点的分数必须获取所有它连接的点的分数。

  所以就是最大权闭合子图辣。不过由于一个植物可能会保护它自己,或者保护(保护它的植物),所以判环,环与环到达的点统统不拿来建图。建个反图跑拓扑排序就好了。

  代码:

#include <bits/stdc++.h>
#define maxn 720005
#define inf 0x3f3f3f
using namespace std;
long long n,m,tot=1,ans=0;
queue <int > q;
int head[maxn],nex[maxn],to[maxn],edge[maxn],cur[maxn];
int mapp[300][300],vis[maxn],lev[maxn];
int attack[605][605],start,endn,cango[maxn];
int in[maxn];
void add(int x,int y,int z){
    to[++tot]=y;edge[tot]=z;nex[tot]=head[x];head[x]=tot;
}
int turn(int x,int y){
    if (x==0) return 0;
    return (x-1)*m+y;
}
int turn1(int x,int y){
    return x*m+y+1;
}//这两个turn是转二维坐标为一维用的,看自己喜欢怎么做来咯
void t_sort(){//拓扑排序
    for (int i=2;i<=tot;i++){
        in[to[i]]++;
    }
    for (int i=1;i<=n*m;i++) {
        if (!in[i]) {
            q.push(i);
            cango[i]=1;
        }
    }
    while (!q.empty()){
        int x=q.front();
        q.pop();
        cango[x]=1;
        for (int i=head[x];i;i=nex[i]){
            in[to[i]]--;
            if (!in[to[i]]) q.push(to[i]);
        }
    }
}
void build(){//建图
    tot=1;
    memset(head,0,sizeof(head));
    memset(nex,0,sizeof(nex));
    memset(to,0,sizeof(to));
    memset(edge,0,sizeof(edge)); 
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            int x=turn(i,j);
            if (!cango[x]) continue;
            if (j<m) {
                add(x,x+1,inf);
                add(x+1,x,0);
            }
            for (int l=1;l<=attack[x][0];l++){
                int y=attack[x][l];
                if (!cango[y]) continue;
                add(y,x,inf);add(x,y,0); 
            }
        }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            int x=turn(i,j);
            if (!cango[x]) continue;
            if (mapp[i][j]>0)
                add(start,x,mapp[i][j]),add(x,start,0),ans+=mapp[i][j];
            if (mapp[i][j]<0)
                add(x,endn,-mapp[i][j]),add(endn,x,0);
        }
}
void f_build(){//建反图,反图只用建原图中的边就好了
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            int x=turn(i,j);
            if (j<m) {
                add(x+1,x,inf);
            }
            for (int l=1;l<=attack[x][0];l++){
                int y=attack[x][l];
                add(x,y,inf);
            }
        }
}
int bfs(){//bfs定层数,找有无增广路
    memset(vis,0,sizeof(vis));
    memset(lev,0,sizeof(lev));
    q.push(start);vis[start]=1;
    while (!q.empty()) {
        int x=q.front();
        cur[x]=head[x];
        q.pop();
        for (int i=head[x];i;i=nex[i]) {
            int y=to[i];
            if (edge[i]>0 && !vis[y]) {
                q.push(y);vis[y]=1;lev[y]=lev[x]+1;
            }
        }
    }
    if (!lev[endn]) return 0;
    else return 1;
}
int dfs(int x,int rest){//dfs找增广路(dinic)
    if (x==endn) return rest;
    int sum=0;
    for (int i=cur[x];i;i=nex[i]){
        int y=to[i];
        cur[x]=i;
        if (edge[i]>0 && lev[y]==lev[x]+1){
            int d=dfs(y,min(edge[i],rest-sum));
            sum+=d;edge[i]-=d;edge[i^1]+=d;
        }
        if (sum==rest) break;
    }
    if (!sum) lev[x]=0;
    return sum;
}
void dinic(){
    while (bfs()){
        ans-=dfs(start,inf);
    }
}
int main(){

    scanf("%lld%lld",&n,&m);
    start=n*m+1;endn=n*m+2;
    for (int i=1;i<=n;i++) {
        for (int j=1;j<=m;j++){
            int p,w;
            scanf("%d%d",&p,&w);
            mapp[i][j]=p;
            int x=turn(i,j);
            attack[x][0]=w;
            for (int l=1;l<=w;l++) {
                int z,z1;
                scanf("%d%d",&z,&z1);
                attack[x][l]=turn1(z,z1);
            }
        }
    }
    f_build();
    t_sort();
    build();
    dinic();
    printf("%lld\n",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Illusions/p/13498420.html