zoukankan      html  css  js  c++  java
  • 题解 [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;
    }
  • 相关阅读:
    [摘录]C++ GUI库大全
    ascii 表
    ubuntu 7.04 Feisty Fawn 安装手记之二:基本配置
    二叉排序树之删除结点
    二叉树与数组
    二叉树删除,重建,交换
    二叉树判断相等,复制
    链表扩展是否有环及环的第一个结点
    二分查找
    二叉排序树之按大小遍历
  • 原文地址:https://www.cnblogs.com/Illusions/p/13498420.html
Copyright © 2011-2022 走看看