BZOJ 1001 狼抓兔子 [最小割转最短路=平面图转对偶图]

原题链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1001


参考资料:

最大最小定理(平面图最小割 对偶图)周冬


【思考】
这题就是一道裸的最小割[什么你看不出来],求最小割去多少边,使得起点与终点不连通。
于是你就试着写流(滑稽。

===========================乌龟天空游,火星撞地球

什么你写流过了(滑稽。
让我们来算一算,n,m<=1000,最多有999*1000+1000*999+999*999=2996001条边,1000*1000=1000000个点,这样跑一个流。
/*
老师告诉我们:写作文要引用名人名言!
lbn187大神曾经说过:网络流跑200000万条边完全没问题!!!
所以写流是可以过的(滑稽
*/
不TLE才怪。

这是一条普通的分割线===========================

这时候我们需要一个更加快速的方法来算最小割。
这里用到了把最小割转换成最短路,把平面图转换成对偶图的方法。主要思想如下:
①把每一个图中的面积块当作新的点。
②每一条边都与两个面相连,把面看作点后,这条边连接这两个面化作的点,权值不变。
③连接起点与终点,把外面的最大平面分成两份,分别为最短路的起点与终点。
④跑一边最短路即可。

附图:

可以看出每一条从0到13的点都代表了一种割法。所以最短路和最小割等价。
另外,本题图长什么样给你了,比较方便。有些题只告诉你是平面图,面和点要自己找,如果遇到,请至网上自行找板子。

附程序:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <windows.h> 
#define rep(j,k,l) for (int j=k;j<=l;j++) 

using namespace std;
int n,m,nn,mm,v[7000000][2],ne[7000000],st[2500000],lych[2500000],num[2500000];
bool used[2500000];

void add(int k,int l,int o){
	
	v[++mm][1]=l;
	v[mm][0]=o;
	ne[mm]=st[k];
	st[k]=mm;	
	v[++mm][1]=k;
	v[mm][0]=o;
	ne[mm]=st[l];
	st[l]=mm;
	
}

int main(){
	
	
	scanf("%d%d",&n,&m);
	nn=(n-1)*(m-1)*2+1;mm=0;
	rep(i,1,n)
		rep(j,1,m-1){
			
			int k;
			scanf("%d",&k);
			if (i==1) add(j*2,nn,k);
			else if (i==n) add((n-2)*(m-1)*2+j*2-1,0,k);
			else add((i-2)*(m-1)*2+j*2-1,(i-1)*(m-1)*2+j*2,k);
			
		}
		
	rep(i,1,n-1)
		rep(j,1,m){
			
			int k;
			scanf("%d",&k);
			if (j==1) add(0,(i-1)*(m-1)*2+1,k);
			else if (j==m) add(nn,i*(m-1)*2,k);
			else add((i-1)*(m-1)*2+(j-1)*2,(i-1)*(m-1)*2+(j-1)*2+1,k);
			
		}
		
	rep(i,1,n-1)
		rep(j,1,m-1){
			
			int k;
			scanf("%d",&k);
			add((i-1)*(m-1)*2+j*2-1,(i-1)*(m-1)*2+j*2,k);
			
		}
	
	/*rep(i,0,nn){
		
		printf("%d\n",i);
		for (int j=st[i];j!=0;j=ne[j])
			printf("%d %d\n",v[j][1],v[j][0]);
		
	}*/
		
	int head=0,tail=1;lych[1]=0;used[0]=1;num[0]=1;
	while (head!=tail){
		
		head=(head+1)%2000000;int k=lych[head];used[k]=0;
		for (int i=st[k];i!=0;i=ne[i]){
			
			int l=v[i][1];
			if (num[l]==0||num[l]>num[k]+v[i][0]){
				
				num[l]=num[k]+v[i][0];
				if (used[l]==0){
					
					tail=(tail+1)%2000000;
					lych[tail]=l;
					
				}
				
			}
			
		}
		
	}
	printf("%d\n",num[nn]-1);
	system("pause");
	return 0;
	
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值