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

你可能感兴趣的:(最短路,网络流,最小割,bzoj)