题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入输出格式
输入格式:
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式:
程序运行结束时,将取数的最大总和输出
输入输出样例
输入样例#1:
3 3
1 2 3
3 2 3
2 3 1
输出样例#1:
11
分析:这是一道经典的网络流模板题,我们先将格子黑白染色,假使我们选中一个格子,那么与之冲突的格子一定是不同颜色的,所以我们把所有黑格向源点连边,容量是格子里所带的权值,所有的白格向汇点连边,容量也是权值;每个黑格向与之有冲突的的白格连边,跑一遍最大流就好了,然而需要注意的是:最后的答案是所有权值之和-最大流
我在这里尝试的解释一下这么做的正确性,虽然算的是最大流,但换句话说就是最小割,我们割掉的边是权值最小的遍,所以最后的答案要用所有权值之和-最大流(最小割)
这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
using namespace std;
const int INF=10000001;
struct node{
int x,y,v,next;
};
node way[8001];
int n,m,st[4001],map[201][201],s,t;
int deep[4001],tot=-1,tt=0;
bool p[4001];
int zz[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int add(int u,int vv,int w)
{
tot++;
way[tot].x=u; //正向边 边权=容量-当前流量
way[tot].y=vv;
way[tot].v=w;
way[tot].next=st[u];
st[u]=tot;
tot++;
way[tot].x=vv; //反向边 边权=当前流量
way[tot].y=u;
way[tot].v=0;
way[tot].next=st[vv];
st[vv]=tot;
}
int bh(int h,int z)
{
return m*(h-1)+z;
}
void lianbian()
{
int i,j;
s=0;
t=n*m+1;
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
{
if ((i+j)%2==1)
{
add(s,bh(i,j),map[i][j]);
for (int l=0;l<4;l++)
{
int xx=i+zz[l][0];
int yy=j+zz[l][1];
if (xx>0&&yy>0&&xx<=n&&yy<=m)
add(bh(i,j),bh(xx,yy),INF);
}
}
else
add(bh(i,j),t,map[i][j]);
}
}
}
int bfs(int s,int t) //分层 搜索时搜的都是正向边
{
int i;
memset(deep,0x7f,sizeof(deep));
memset(p,1,sizeof(p)); //判断点是否遍历过
queue<int> q;
q.push(s);
deep[s]=1;
p[s]=0;
while (!q.empty())
{
int r=q.front();
q.pop();
for (i=st[r];i!=-1;i=way[i].next)
if (p[way[i].y]&&way[i].v) //判断一下此边是否可走
{
p[way[i].y]=0;
deep[way[i].y]=deep[r]+1;
q.push(way[i].y);
}
}
return deep[t]<0x7f; //如果deep[t]==0x7f,说明无路可增广了
}
int dfs(int now,int t,int limit) //limit是可以增广的流量,因为要取min,所以传进来的是INF
{
int i;
if (limit==0||now==t) //剪枝
return limit;
int flow=0; //这张增广网上的总流量
int f;
for (i=st[now];i!=-1;i=way[i].next)
{
if (deep[way[i].y]==deep[now]+1&&(f=dfs(way[i].y,t,min(limit,way[i].v))))
{ //递归放在if中!!!
flow+=f;
limit-=f; //?记住就好了
way[i].v-=f;
way[i^1].v+=f;
if (!limit) break; //!!!
}
}
return flow;
}
int dinic()
{
int ans=0;
while (bfs(s,t)) //有继续增广的潜力
ans+=dfs(s,t,INF);
return ans;
}
int main()
{
memset(st,-1,sizeof(st));
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&map[i][j]),tt+=map[i][j];
lianbian();
printf("%d",tt-dinic());
return 0;
}