zoukankan      html  css  js  c++  java
  • HDU 1569 方格取数(2) (Dinic)

    方格取数(2)

    Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
    Total Submission(s): 3387    Accepted Submission(s): 1045


    Problem Description
    给你一个m*n的格子的棋盘,每个格子里面有一个非负数。
    从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大。
     
    Input
    包括多个测试实例,每个测试实例包括2整数m,n和m*n个非负数(m<=50,n<=50)
     
    Output
    对于每个测试实例,输出可能取得的最大的和
     
    Sample Input
    3 3 75 15 21 75 15 28 34 70 5
     
    Sample Output
    188
     
    Author
    ailyanlu
     
    Source
     
    Recommend
    8600
     

    给出一个 N * M 的矩阵,每个格放着一个非负数,要求选出一些数,使他们的和最大,要求是有相邻边的格子里的数不能同时选。

    先说,我压根没想过这事网络流……因为方格取数(1)是个状态压缩……

    看了题解,才明白的:

    这个题由于数据范围较大,所以状态压缩过不去,需要用网络流,我重复一遍人家的建图:

    我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有:

    最大点权独立集 + 最小点权覆盖集 = 总点权和,

    这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图,

    1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一条边,权值为该黑色点的权值,

    2,从白色的点向汇点连一条边,权值为该白色点的权值,

    3,然后,对于每一对相邻的黑白点,从黑点向白点连一条边,权值为无穷大。

    最后求最小割(最大流),即为最小点权覆盖集。

    因为我们求出的最小割集一定是从那些相邻的黑白点之间的边(也就是不能用的边,因为相邻的数不能同时选)中选出来的,且是最小代价,也就是说从方格中拿掉的数之和尽量小,那么剩下的数之和一定是最大的。

    我只能说,神奇的网络流!!!!Orz!!!!

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    
    using namespace std;
    
    const int VM=2520;
    const int EM=500010;
    const int INF=0x3f3f3f3f;
    
    struct Edge{
        int u,v,nxt;
        int flow;
    }edge[EM<<1];
    
    int n,m,cnt,head[VM];
    int src,des,dep[VM];
    
    void addedge(int cu,int cv,int cf){
        edge[cnt].u=cu;  edge[cnt].v=cv;  edge[cnt].flow=cf;
        edge[cnt].nxt=head[cu];  head[cu]=cnt++;
    
        edge[cnt].u=cv;  edge[cnt].v=cu;  edge[cnt].flow=0;
        edge[cnt].nxt=head[cv];  head[cv]=cnt++;
    }
    
    int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
    
    int legal(int i,int j,int k){
        int x=i+dir[k][0];
        int y=j+dir[k][1];
        return x>=1 && x<=n && y>=1 && y<=m;
    }
    
    int BFS(){  // 重新 建 图 (按 层数 建图)
        queue<int> q;
        while(!q.empty())
            q.pop();
        memset(dep,-1,sizeof(dep));
        dep[src]=0;
        q.push(src);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            for(int i=head[u];i!=-1;i=edge[i].nxt){
                int v=edge[i].v;
                if(edge[i].flow>0 && dep[v]==-1){  // 如果 可以  可以到达 但 还没有 访问
                    dep[v]=dep[u]+1;
                    q.push(v);
                }
            }
        }
        return dep[des]!=-1;
    }
    /*
    int DFS(int u,int minx){  // 查找  路径上的 最小 的 流量
         if(u==des)
             return minx;
         int tmp;
         for(int i=head[u];i!=-1;i=edge[i].nxt){
             int v=edge[i].v;
             if(edge[i].flow>0 && dep[v]==dep[u]+1 && (tmp=DFS(v,min(minx,edge[i].flow)))){
                 edge[i].flow-=tmp;
                 edge[i^1].flow+=tmp;
                 return tmp;
             }
             //printf("!!!!!!\n");
         }
         return 0;
     }
    */
    
    int DFS(int u,int minx){
        int ans=0;
        if(u==des)
            return minx;
        for(int i=head[u];i!=-1 && ans<minx;i=edge[i].nxt){
            int v=edge[i].v;
            if(edge[i].flow>0 && dep[v]==dep[u]+1){
                int tmp=min(edge[i].flow,minx-ans);
                tmp=DFS(v,tmp);
                ans+=tmp;
                edge[i].flow-=tmp;
                edge[i^1].flow+=tmp;
            }
        }
        if(!ans)
            dep[u]=-2;
        return ans;
    }
    
    int Dinic(){
        int ans=0,tmp;
        while(BFS()){
            while(1){
                tmp=DFS(src,INF);
                if(tmp==0)
                    break;
                ans+=tmp;
            }
        }
        return ans;
    }
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        while(~scanf("%d%d",&n,&m)){
            cnt=0;
            memset(head,-1,sizeof(head));
            int x,sum=0;
            src=0;  des=n*m+1;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++){
                    scanf("%d",&x);
                    sum+=x;
                    if((i+j)%2==0){
                        addedge(src,(i-1)*m+j,x);
                        for(int k=0;k<4;k++){
                            if(legal(i,j,k))
                                addedge((i-1)*m+j,(i+dir[k][0]-1)*m+(j+dir[k][1]),INF);
                        }
                    }else{
                        addedge((i-1)*m+j,des,x);
                        for(int k=0;k<4;k++){
                            if(legal(i,j,k))
                                addedge((i+dir[k][0]-1)*m+(j+dir[k][1]),(i-1)*m+j,INF);
                        }
                    }
                }
            int maxflow=Dinic();
            printf("%d\n",sum-maxflow);
        }
        return 0;
    }
  • 相关阅读:
    CCCC 2020 酱油记
    CCPC 2020 威海 滚粗记
    IEEExtreme 2020 酱油记
    CCSP 2020 酱油记
    ICPC 陕西省赛 2020 游记
    CCPC 网络赛 2020 自闭记
    CSP 第20次认证 酱油记
    CSP-S 2019 酱油记
    NOI2019 退役记
    树链剖分入门
  • 原文地址:https://www.cnblogs.com/jackge/p/3114494.html
Copyright © 2011-2022 走看看