题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
程序运行结束时,将取数的最大总和输出
思路:题目要求求出一些点,是每个点之间没有边相连,且点权最大。想到二分图最小点权覆盖,最小点权覆盖=最小割。答案为所有点权-最小割=所有点权-最大流。 根据题意,把上下左右相邻的点黑白染色。源点S向白色的点加边,容量为对应点权,黑色点向汇点T加边,容量为点权,白色的点向相邻黑色点加边,容量为无穷。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<queue> #define inf 0x3fffffff using namespace std; const int maxn=2777; int first[maxn],sign,cur[maxn]; int s,t,d[maxn]; struct node { int to,w,next; } edge[maxn*20]; void creat() { memset(first,-1,sizeof(first)); sign=0; } void add_edge(int u,int v,int w) { edge[sign].to=v; edge[sign].w=w; edge[sign].next=first[u]; first[u]=sign++; edge[sign].to=u; edge[sign].w=0; edge[sign].next=first[v]; first[v]=sign++; } int bfs() { queue<int>q; memset(d,0,sizeof(d)); d[s]=1; q.push(s); while(!q.empty()) { int top=q.front(); q.pop(); for(int i=first[top]; ~i; i=edge[i].next) { int to=edge[i].to; if(edge[i].w>0&&d[to]==0) { d[to]=d[top]+1; if(to==t) return 1; q.push(to); } } } return d[t]!=0; } int dfs(int top,int flow) { if(top==t) return flow; int ans=0,x=0; for(int i=cur[top]; ~i; i=edge[i].next) { int to=edge[i].to; if(edge[i].w>0&&d[to]==d[top]+1) { x=dfs(to,min(flow-ans,edge[i].w)); edge[i].w-=x; edge[i^1].w+=x; if(edge[i].w) cur[top] = i; ans+=x; if(ans==flow) return flow; } } if(ans==0) d[top]=0; return ans; } int dinic(int n) { int ans=0; while(bfs()) { for(int i=0; i<=n; i++) cur[i]=first[i]; ans+=dfs(s,inf); } return ans; } int a[111][111]; int go[4][2]= {1,0,0,1,-1,0,0,-1}; int main() { int n,m; scanf("%d%d",&n,&m); creat(); int sum=0; for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&a[i][j]),sum+=a[i][j]; s=0,t=n*m+1; for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if((i+j)&1) add_edge(s,(i-1)*m+j,a[i][j]); else add_edge((i-1)*m+j,t,a[i][j]); for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) if((i+j)&1) for(int k=0; k<4; k++) { int x=i+go[k][0],y=j+go[k][1]; if(x>=1&&x<=n&&y>=1&&y<=m) { int res=(x-1)*m+y; add_edge((i-1)*m+j,res,inf); } } printf("%d\n",sum-dinic(t)); return 0; }