CodeVS1907 方格取数3

CodeVS1907 方格取数3
题目描述 Description

«问题描述:
在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任
意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。
«编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

输入描述 Input Description

第1 行有2 个正整数m和n,分别表示棋盘的行数
和列数。接下来的m行,每行有n个正整数,表示棋盘方格中的数。

输出描述 Output Description

将取数的最大总和输出

样例输入 Sample Input

3 3
1 2 3
3 2 3
2 3 1

样例输出 Sample Output

11

数据范围及提示 Data Size & Hint

n,m<=30

分析

        要从中取一些数,使得和最大,可以认为去掉一些数,使得满足任意两个剩下的输不相邻且和最大。构建网络流图,首先对网格进行黑白染色,S连向所有的白点,容量等于格子中数的大小,所有黑点连向T,容量也等于格子中的数字,然后对于所有的白点,向它相邻的黑点连边,容量为+∞。
       我们来看这张图,可以把所有的边分为三部分,A是一条从S连向白点的边,B是一条白点连向黑点的边,C是一条黑点连向T的边。假如存在一条路径使得从S点能走到T,这条路径是A→B→C,则它一定经过了一个白点和一个黑点,也一定经过了一条容量为+∞的边(B),而经过这样的边就意味着你选择了相邻的格点,这是不被允许的,所以我们可以不选择白点或不选择黑点,对应的方法就是割去边A或割去边C,当然也可以同时割去,但如果割去边B就没有意义了。
      为了使不存在不合法的边,需要割掉一些边,其中不包含B类边且容量和最小,就要求出这张图的最小割,由于我们有最大流=最小割定理,所以要求的最小割就等于最大流(用ISAP求出)。加入最大流失flow,所有格点的权值和为SUM,答案就是SUM-flow。
      一个小点(查错查了一上午):给格子进行黑白染色,并不是简单地黑白黑白黑白。。。。当M为偶数时,每一行的最后一个格子的颜色和下一行的第一个格子的颜色是相同的。

代码

#include <cstdio>
#include <algorithm>
#define inf 0x3f3f3f3f
#define maxn 1000
using namespace std;
int N, M, ans, head[maxn], next[maxn<<3], to[maxn<<3], c[maxn<<3], tot=1, S=0, T=999,
	dist[maxn], last[maxn], num[maxn], SUM;
bool Exit;
void add(int a, int b, int v)
{to[++tot]=b;c[tot]=v;next[tot]=head[a];head[a]=tot;last[a]=head[a];}
void input()
{
	int i, j, x, t=0, color=0;
	scanf("%d%d",&N,&M);
	for(i=1;i<=N;i++)
		for(j=1;j<=M;j++)
		{
			if(j==1){color=(M&1)?!color:color;}
			else color=!color;
			scanf("%d",&x);SUM+=x;
			t++;
			if(color)
			{
				add(S,t,x),add(t,S,0);
				if(i>1)add(t,t-M,inf),add(t-M,t,0);
				if(i<N)add(t,t+M,inf),add(t+M,t,0);
				if(j>1)add(t,t-1,inf),add(t-1,t,0);
				if(j<M)add(t,t+1,inf),add(t+1,t,0);
			}
			else add(t,T,x),add(T,x,0);
		}
}
int dfs(const int x, const int in)
{
	if(x==T)return in;
	int p, t, ans=0;
	for(p=last[x];p;last[x]=p=next[p])
		if(c[p]&&dist[x]==dist[to[p]]+1)
		{
			ans+= t=dfs(to[p],min(c[p],in-ans));
			c[p]-=t;c[p^1]+=t;
			if(Exit||ans==in)return ans;
		}
	Exit = --num[dist[x]] ==0; num[++dist[x]]++;
	last[x]=head[x];
	return ans;
}
int main()
{
	int flow=0;
	input();
	num[0]=N*M+2;
	while(!Exit)flow+=dfs(S,inf);
	printf("%d\n",SUM-flow);
	return 0;
}


你可能感兴趣的:(CodeVS1907 方格取数3)