题目大意:
在一个有 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。
思路:
这道题明显是一个二分图。我们可以把每个点染色,将为偶数的点连向,否则连向。那么对于每一个连的白点,将它连向它周围的四个黑点,容量为。跑一边最大流即可。
代码:
#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
#define Inf 0x7f
#define INF 1e9
using namespace std;
int n,m,s,t,x,y,k,head[200001],dep[200001],ans,sum,num;
struct edge
{
int next,to,c;
}e[200001];
void add(int from,int to,int c)
{
k++;
e[k].to=to;
e[k].c=c;
e[k].next=head[from];
head[from]=k;
}
bool bfs()
{
memset(dep,Inf,sizeof(dep));
dep[s]=0;
queue<int> q;
q.push(s);
while (q.size())
{
int u=q.front();
q.pop();
for (int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if (dep[v]>dep[u]+1&&e[i].c)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]<Inf;
}
int dfs(int u,int low)
{
int lows=0;
if (u==t) return low;
for (int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if (dep[v]==dep[u]+1&&e[i].c)
{
lows=dfs(v,min(e[i].c,low));
if (lows)
{
e[i].c-=lows;
e[i^1].c+=lows;
return lows;
}
}
}
return 0;
}
//以上为最大流模板
int main()
{
scanf("%d%d",&n,&m);
k=1;
s=n*m+4;
t=n*m+5;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&x);
num+=x;
y=i*m-m+j;
if (!((i+j)%2)) //黑点
{
add(y,t,x);
add(t,y,0);
}
else //白点
{
add(s,y,x);
add(y,s,0);
if (i>1) //连上面的黑点
{
add(y,y-m,INF);
add(y-m,y,0);
}
if (i<n) //连下面的黑点
{
add(y,y+m,INF);
add(y+m,y,0);
}
if (j>1) //连左边的黑点
{
add(y,y-1,INF);
add(y-1,y,0);
}
if (j<m) //连右边的黑点
{
add(y,y+1,INF);
add(y+1,y,0);
}
}
}
while (bfs())
{
while (sum=dfs(s,INF+1))
ans+=sum;
}
printf("%d\n",num-ans);
return 0;
}