BZOJ 1001: [BeiJing2006]狼抓兔子

题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=1001


题目大意:见原题&算法讨论。


算法讨论:

        很容易看出这是最小割。

        我们首先想到的是最小割=最大流。

        但是点数最多有1e6,用O(n^2*m)的Dinic和O(n*m^2)的sap显然都是要超时的。(据说Dinic也能过?)

        于是我们想到了另一个公式:平面图最大流=对偶图最短路。

        网格图做对偶图是很方便的啦~

        然后SPFA就更方便了啦~


Code:

/*
 * Problem:1001
 * Author:PYC
 */

#include <cstdio>

#define Vnum (1000*1000*2)
#define Enum (1000*1000*5*2)
#define maxn 2000100
#define oo (1<<30)
#define Bnum 1100
#define ooo 2147483647 

using namespace std;

int n,m,mm,son[Vnum],next[Enum],ed[Enum],data[Enum],end,h=0,t=1,q[maxn],dis[maxn],tt=1,id[Bnum][Bnum][2];
bool v[maxn];

inline int min(int x,int y){
	return x<y?x:y;
}

inline void INSERT(int x,int y,int z){
	mm++;
	next[mm]=son[x];
	son[x]=mm;
	ed[mm]=y;
	data[mm]=z;
}

inline void insert(int x,int y,int z){
	INSERT(x,y,z);
	INSERT(y,x,z);
}

void spfa(){
	for (int i=2;i<=end;++i) dis[i]=oo;
	v[1]=1;
	q[1]=1;
	while (h!=t){
		h++;
		h%=maxn;
		v[q[h]]=0;
		int x=q[h];
		for (int i=son[x];i;i=next[i]){
			int y=ed[i];
			if (dis[x]+data[i]<dis[y]){
				dis[y]=dis[x]+data[i];
				if (!v[y]){
					t++;
					t%=maxn;
					v[y]=1;
					q[t]=y;
				}
			}
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	if (n==1 && m==1){
		printf("0\n");
		return 0;
	}
	if (n==1){
		int ans=ooo;
		for (int i=1;i<=m-1;++i){
			int x;
			scanf("%d",&x);
			ans=min(ans,x);
		}
		printf("%d\n",ans);
		return 0;
	}
	if (m==1){
		int ans=ooo;
		for (int i=1;i<=n-1;++i){
			int x;
			scanf("%d",&x);
			ans=min(ans,x);
		}
		printf("%d\n",ans);
		return 0;
	}
	for (int i=1;i<=n-1;++i)
		for (int j=1;j<=m-1;++j){
			++tt;
			id[i][j][0]=tt;
			++tt;
			id[i][j][1]=tt;
		}
	end=tt+1;
	for (int i=1;i<=n;++i)
		for (int j=1;j<=m-1;++j){
			int x;
			scanf("%d",&x);
			if (i==1){
				insert(1,id[i][j][0],x);
				continue;
			}
			if (i==n){
				insert(end,id[i-1][j][1],x);
				continue;
			}
			insert(id[i][j][0],id[i-1][j][1],x);
		}
	for (int i=1;i<=n-1;++i)
		for (int j=1;j<=m;++j){
			int x;
			scanf("%d",&x);
			if (j==1){
				insert(end,id[i][j][1],x);
				continue;
			}
			if (j==m){
				insert(1,id[i][j-1][0],x);
				continue;
			}
			insert(id[i][j-1][0],id[i][j][1],x);
		}
	for (int i=1;i<=n-1;++i)
		for (int j=1;j<=m-1;++j){
			int x;
			scanf("%d",&x);
			insert(id[i][j][0],id[i][j][1],x);
		}
	spfa();
	printf("%d\n",dis[end]);
	return 0;
}

By Charlie Pan

Mar 15,2014

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值