Description

在一个有m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。

编程任务:
对于给定的方格棋盘,按照取数要求编程找出总和最大的数。

Input

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

Output

程序运行结束时,将取数的最大总和输出

Sample Input

3 3
1 2 3
3 2 3
2 3 1

Sample Output

11


题目链接:

方格取数问题


这个题是一个很经典的问题:最大点权问题

二分图最大点权独立集问题,就是找出图中一些点,使得这些点之间没有边相连,这些点的权值之和最大。
独立集与覆盖集是互补的,求最大点权独立集可以转化为求最小点权覆盖集(最小点权支配集)。最小点权覆盖集问题可以转化为最小割问题解决。
结论:最大点权独立集 = 所有点权 - 最小点权覆盖集 = 所有点权 - 最小割集 = 所有点权 - 网络最大流。

二分图最大点权独立集问题,更多讨论见《最小割模型在信息学竞赛中的应用》作者胡伯涛


这里不会证明

说说思路:把这个图黑白染色,使得任意一个黑点的相邻四个点是白点,任意一个白点的相邻四个点是黑点

添加源S和汇T

S向所有的黑点连接一条容量为黑点格子中数值的有向边(很经典的思路,化格子中的点权为S的边权)

所有的黑点向T连接一条容量为白点格子中数值的有向边

然后,对于相邻的两个点,从黑点向白点连接一条容量为INF的有向边


这里,就只贴出来黑白染色的代码和连边的代码了

int main(){
	//freopen("input.txt","r",stdin);
	int m,n,s,t,tot,ans=0,x,y;
	init();
	scanf("%d%d",&n,&m);
	s=n*m;t=n*m+1;tot=n*m+2;x=0;
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			if (i%2==0)
				num[i][j]=x++;
			else
				num[i][m-j-1]=x++;
	//for(int i=0;i<n;i++)
	//	for(int j=0;j<m;j++)
	//		printf("%d%c",num[i][j],j==m-1?'\n':' ');
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++)
			scanf("%d",&mp[i][j]);
	//for(int i=0;i<n;i++)
	//	for(int j=0;j<m;j++)
	//		printf("%d%c",mp[i][j],j==m-1?'\n':' ');
	for(int i=0;i<n;i++)
		for(int j=0;j<m;j++){
			ans+=mp[i][j];
			if (num[i][j]%2==0){
				if (i<n-1) addedge(num[i][j],num[i+1][j],INF);
				if (j<m-1) addedge(num[i][j],num[i][j+1],INF);
				if (i>0) addedge(num[i][j],num[i-1][j],INF);
				if (j>0) addedge(num[i][j],num[i][j-1],INF);
				addedge(s,num[i][j],mp[i][j]);
			}
			else{
				addedge(num[i][j],t,mp[i][j]);
			}
		}
	//printf("%d\n",ans);
	int maxflow=dinic(s,t,tot);
	//printf("%d\n",maxflow);
	printf("%d\n",ans-maxflow);
	return 0;
}