在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
这个题目也是很明显的网络流,奇偶性建图,最后总和-最小割即为答案
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
const int N=10100,M=202000,INF=(1<<29);
int tt=1,n,m,s,t,head[N],dis[N],vis[N],dep[N];
long long maxf=0;
struct pr {int next,to,w;}a[M];
inline int read ()
{
int s=0;
char ch=getchar ();
while (ch>'9'||ch<'0') ch=getchar ();
while (ch>='0'&&ch<='9') {s=s*10+ch-'0';ch=getchar ();}
return s;
}
void add (int u,int v,int w)
{
a[++tt].next=head[u];a[tt].to=v;a[tt].w=w;head[u]=tt;
a[++tt].next=head[v];a[tt].to=u;a[tt].w=0;head[v]=tt;
}
int dinic (int x,int flow)
{
if (x==t) return flow;
int rest=flow,k,y;
for (int i=head[x];i;i=a[i].next) {
y=a[i].to;
if (a[i].w&&dep[y]==dep[x]+1) {
k=dinic (y,min (rest,a[i].w));
if (!k) dep[y]=0;//剪枝
a[i].w-=k;
a[i^1].w+=k;
rest-=k;
}
}
return flow-rest;
}
bool bfs ()
{
int x,y;
queue <int> q;
for (int i=1;i<=n*m+2;i++) dep[i]=0;
q.push(s);dep[s]=1;
while (!q.empty()) {
x=q.front();q.pop();
for (int i=head[x];i;i=a[i].next) {
y=a[i].to;
if (a[i].w&&!dep[y]) {
dep[y]=dep[x]+1;
q.push(y);
// if (y==t) return 1;
}
}
}
return dep[t];
}
int x[]={1,0,-1,0};
int y[]={0,1,0,-1};
int main ()
{
// freopen("y.txt","r",stdin);
n=read(),m=read();
s=m*n+1,t=n*m+2;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) {
int w=read();
maxf+=w;
if((i+j)%2==0) add(s,(i-1)*m+j,w);
else add((i-1)*m+j,t,w);
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j) {
if((i+j)%2==0) {
for(int k=0;k<=3;++k) {
int r=i+x[k],c=j+y[k];
if(r<=n&&r>=1&&c<=m&&c>=1)
add((i-1)*m+j,(r-1)*m+c,INF);
}
}
}
int in;
while (bfs ()) {while ((in=dinic (s,INF))) maxf-=in;}
cout<<maxf;
return 0;
}