1. 程式人生 > >網絡流 24題 方格取數

網絡流 24題 方格取數

題目 struct 同時 class memset 輸出格式 網絡流 越界 structure

方格取數問題

題目描述

在一個有m*n個方格的棋盤中,每個方格中有一個正整數。現要從方格中取數,使任意2個數所在方格沒有公共邊,且取出的數的總和最大。試設計一個滿足要求的取數算法。

輸入格式

文件第1行有2個正整數m和n,分別表示棋盤的行數和列數。接下來的m行,每行有n個正整數,表示棋盤方格中的數。(0 <= m, n <= 30)

輸出格式

取數的最大總和.

輸入樣例

33
1 2 3
3 2 3
2 3 1

輸出樣例

11

題目大意:

給出m*n的格子,相鄰的格子的值不可同時取,最後求出最大值。

我還誤以為只有兩種情況,用暴力不就好了嗎,不過wyy給我舉出其他情況,事實證明我想少了。

構圖:

技術分享

如圖,我們會發現,黑色格子都是可以同時選的,白色格子也是同時可以走的。會發現同是黑色格子(白色格子),它們的行列坐標加起來mod 2,都是一樣的。

所以,我們可以用二分圖來給它們歸類。首先,行列之和,為偶數的放在左邊,行列之和為奇數的放在右邊。於是,便得出,黑色格子的放在右邊,白色格子放在左邊。

因為黑白之間是互不可取的,以中間的黑色格子為例,它不可達的格子有它上下左右的白色格子,於是把它們相連。同理,所有黑色格子都這麽連。

並把黑色格子(行列和為偶數)與s節點相連,白色格子與t相連。

解題思想:

求出來的為最小割,也就是說,如果經過這個點,那就是把這個點割去了,不能選。而得出最小割,也就是得出了不選哪些格子,所以,用總和減去最小割就是最後的答案。

細節:

我們一開始一直wrong,是因為數組開小了,maxn直接開為35,可是在這一個棋盤裏面,格子數是遠遠不止這麽多的,要開到n^2。

代碼如下:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=1000,oo=10000000;//註意數組大小,格子數為n^2。 
int ans;
int cur=-1,s,t,m,n;
int head[maxn],c[maxn][maxn],v[maxn],id[maxn][maxn],a[maxn][maxn];
int xx[5]={0,0,1,-1},yy[5]={1,-1,0,0};

struct space
{
	int to,next,va,type;
}edge[maxn*maxn];

void add(int from,int to,int va,int type)
{
	cur++;
	edge[cur].to=to;
	edge[cur].va=va;
	edge[cur].type=type;
	edge[cur].next=head[from];
	head[from]=cur;
}

void build(int x,int y)
{
	for(int i=0;i<4;i++)
	{
		int nowx=x+xx[i],nowy=y+yy[i];//上下左右點的坐標 
		
		if(nowx<1||nowx>m||nowy<1||nowy>n) continue;//判斷是否有出現越界的情況 
		
		add(id[x][y],id[nowx][nowy],oo,0);//相連 
		add(id[nowx][nowy],id[x][y],0,1);
		
	}
}

void init()
{
	memset(head,-1,sizeof(head));
	int k=0;
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			ans+=a[i][j];
			k++;
			id[i][j]=k;
			c[i][j]=(i%2==j%2);//記下這個格子行列之和為偶數或是奇數 
		}
	}
	s=0,t=n*m+1; 
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(c[i][j])//如果為偶數,即圖中的黑色格子 	
				build(i,j);
		}
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(c[i][j]) 
			{
				add(s,id[i][j],a[i][j],0);//黑色格子與s相連,邊權為a[i][j] 
				add(id[i][j],s,0,1);
			}
			else
			{
				add(id[i][j],t,a[i][j],0);
				add(t,id[i][j],0,1);
			}
		} 
	} 
}

int dfs(int now,int mi)
{
	if(now==t) return mi;
	if(v[now]==1)	return 0;
	v[now]=1;
	int h=head[now];
	while(h!=-1)
	{
		int to=edge[h].to,va=edge[h].va;
		if(va!=0)
		{
			int k;
			k=dfs(to,min(va,mi));
			if(k!=0)
			{
				edge[h].va-=k;
				edge[h^1].va+=k;
				return k;
			}
		}
		h=edge[h].next;
	}
	return 0;
}//最小割(與最大流的代碼完全是一樣的) 

int main()
{
	freopen("2207.in","r",stdin);
	freopen("2207.out","w",stdout);
	cin>>m>>n;
	init();
	
	while(1)
	{
		memset(v,0,sizeof(v));
		int res;
		res=dfs(0,oo);
		if(res==0) break;
		ans-=res;
	}		
	cout<<ans<<endl;
	return 0;
}

  

http://blog.csdn.net/u013686535/article/details/77152103

這篇博客寫得比較清楚。

網絡流 24題 方格取數