1. 程式人生 > >【線性規劃與網路流24題 9】方格取數問題

【線性規劃與網路流24題 9】方格取數問題

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;
}