【网络流24题】方格取数问题
Description
在一个有m * n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
Input
第1 行有2 个正整数m和n,分别表示棋盘的行数和列数。
接下来的m行,每行有n个正整数,表示棋盘方格中的数。
Output
将取数的最大总和输出
Sample Input
3 3
1 2 3
3 2 3
2 3 1
Sample Output
11
Hint
数据范围:
1<=N,M<=30
Source
网络流
二分图点权最大独立集, 网络最小割
解析
这算是道网络流的入门题了。
仔细想一下,
如果一个点被选了,那么它周围的四个点一定不会选。
反过来的话,我们可以选出不选的点,
使剩下的点满足要求。
因此,问题就转化成了使不选的点的权值和最小,
所以,将图进行黑白染色,
将一种颜色与源点连接,
流量为点权,
再连与它相邻的四个格子(边界有特判),流量为INF,
然后另一种颜色就连汇点,流量也为它的点权。
这样,源点和汇点就不能连通,
因此,求出最小割,
再用总点权减掉就行了。
上AC代码:
#include<bits/stdc++.h> using namespace std; inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();} return f*sum; } const int INF=0x3f3f3f3f; struct node{ int next,to,w; }e[100001]; int m,n,s,t,sum=0; int a[10001],col[10001]; int dx[4],v[10001],dis[10001]; int head[10001],cnt=1; void add(int x,int y,int w){ e[++cnt].to=head[x]; e[cnt].next=y; e[cnt].w=w; head[x]=cnt; } void makecol(){ queue <int> que; memset(col,0,sizeof(col)); que.push(2); col[2]=1; while(!que.empty()){ int x=que.front(); que.pop(); if(x%n!=2){ int k=x-1; if(!col[k]) col[k]=3-col[x],que.push(k); } if(x%n!=1){ int k=x+1; if(!col[k]) col[k]=3-col[x],que.push(k); } if(x>n+1){ int k=x-n; if(!col[k]) col[k]=3-col[x],que.push(k); } if(x+n<=n*m+1){ int k=x+n; if(!col[k]) col[k]=3-col[x],que.push(k); } } } void build(){ for(int i=2;i<=n*m+1;i++){ if(col[i]==1){ add(i,t,a[i]); add(t,i,0); } else if(col[i]==2){ add(s,i,a[i]); add(i,s,0); int x=i; if(x%n!=2){ int k=x-1; add(x,k,INF);add(k,x,0); } if(x%n!=1){ int k=x+1; add(x,k,INF);add(k,x,0); } if(x>n+1){ int k=x-n; add(x,k,INF);add(k,x,0); } if(x+n<=n*m+1){ int k=x+n; add(x,k,INF);add(k,x,0); } } } /* for(int i=2;i<=cnt;i++){ printf("to=%d next=%d w=%d\n",e[i].to,e[i].next,e[i].w); }*/ } bool bfs(){ queue <int> q; memset(dis,0xff,sizeof(dis)); q.push(s); dis[s]=0; while(!q.empty()){ int x=q.front(); q.pop(); for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(dis[k]>=0||!e[i].w) continue; dis[k]=dis[x]+1; q.push(k); } } if(dis[t]>0) return 1; return 0; } int dfs(int x,int mi){ if(x==t) return mi; int ret; for(int i=head[x];i;i=e[i].to){ int k=e[i].next; if(dis[k]-1!=dis[x]||!e[i].w) continue; if((ret=dfs(k,min(mi,e[i].w)))){ e[i].w-=ret; e[i^1].w+=ret; return ret; } } return 0; } void DINIC(){ int ans=0,ret; while(bfs()){ while((ret=dfs(s,INF))){ ans+=ret; } } printf("%d\n",sum-ans); return ; } int main(){ m=read();n=read(); s=1;t=n*m+2; for(int i=2;i<=n*m+1;i++) a[i]=read(),sum+=a[i]; dx[0]=1;dx[1]=-1;dx[2]=n;dx[3]=-n; makecol(); build(); DINIC(); return 0; }