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个格子不能相邻,并且取出的数的和最大。
从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的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; }