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;
}
原文地址:https://www.cnblogs.com/jackge/p/3114494.html