1. 程式人生 > >BZOJ1001 [BeiJing2006]狼抓兔子(網路流最小割)

BZOJ1001 [BeiJing2006]狼抓兔子(網路流最小割)

題目可以轉化為:從原圖中選出一個邊集,使得去掉它之後,(1,1)與(n,m)不通 
即:以(1,1)為源,(n,m)為匯,求該圖最小割 
不過由於節點過多,直接對輸入的圖求最小割的話會超時 

轉化:求它的對偶圖,然後求最短路,見圖,紅字為點編號,綠線為建的邊(邊權為其經過的原圖的邊的權值)

注意:1.計算出轉化後點數、邊數的範圍(易錯!)

            2.n==1 或 m==1 的特殊情況 

程式碼:

#include<stdio.h>
#include<stdlib.h>
#define INF 1000000000
int u[6000010]={0},v[6000010]={0},w[6000010]={0},first[2000005]={0},next[6000010]={0},d[2000005]={0},q[3000005]={0},hash[2000005]={0};
int e=0;
void tj(int x,int y,int z)
{
	u[++e]=x;
	v[e]=y;
	w[e]=z;
	next[e]=first[x];
	first[x]=e;
}
int main()
{
	int n,m,i,j,s,t,x,y,node,head=0,tail=1,min=INF;
	scanf("%d%d",&n,&m);
	s=2*(n-1)*(m-1)+1;
	node=t=s+1;
	for(i=1;i<=n;i++)
		for(j=1;j<m;j++)
		{
			scanf("%d",&x);
			if(min>x) min=x;
			if(i==1) tj(j,t,x);
			if(i>1&&i<n)
			{
				tj((2*i-2)*(m-1)+j,(2*i-3)*(m-1)+j,x);
				tj((2*i-3)*(m-1)+j,(2*i-2)*(m-1)+j,x);
			}
			if(i==n) tj(s,(2*i-3)*(m-1)+j,x);
		}
	for(i=1;i<n;i++)
		for(j=1;j<=m;j++)
		{
			scanf("%d",&x);
			if(min>x) min=x;
			if(j==1) tj(s,(2*i-1)*(m-1)+1,x);
			if(j>1&&j<m)
			{
				tj((2*i-2)*(m-1)+j-1,(2*i-2)*(m-1)+m+j-1,x);
				tj((2*i-2)*(m-1)+m+j-1,(2*i-2)*(m-1)+j-1,x);
			}
			if(j==m) tj((2*i-1)*(m-1),t,x);
		}
	for(i=1;i<n;i++)
		for(j=1;j<m;j++)
		{
			scanf("%d",&x);
			if(min>x) min=x;
			tj((2*i-1)*(m-1)+j,(2*i-2)*(m-1)+j,x);
			tj((2*i-2)*(m-1)+j,(2*i-1)*(m-1)+j,x);
		}
	if(n==1||m==1) printf("%d",min);//注意特判n或m==1 !!!
	else
	{
		for(i=1;i<=node;i++)
			d[i]=INF;
		d[s]=0;
		q[0]=s;
		hash[s]=1;
		while(head!=tail)
		{
			for(i=first[q[head]];i!=0;i=next[i])
				if(d[v[i]]>d[u[i]]+w[i])
				{
					d[v[i]]=d[u[i]]+w[i];
					if(hash[v[i]]==0)
					{
						hash[v[i]]=1;
						q[tail]=v[i];
						tail++;
						if(tail>=2500000) tail-=2500000;
					}
				}
			hash[q[head]]=0;
			head++;
			if(head>=2500000) head-=2500000;
		}
		printf("%d",d[t]);
	}
	return 0;
}