狼抓兔子 HYSBZ - 1001 (平面图转对偶图最短路求最小割)

狼抓兔子

 HYSBZ - 1001 

现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,

而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:

 

左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下三种类型的道路 

1:(x,y)<==>(x+1,y) 

2:(x,y)<==>(x,y+1) 

3:(x,y)<==>(x+1,y+1) 

道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,

开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下解(N,M)的窝中去,狼王开始伏击

这些兔子.当然为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,

才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的

狼的数量要最小。因为狼还要去找喜羊羊麻烦.

Input

第一行为N,M.表示网格的大小,N,M均小于等于1000.

接下来分三部分

第一部分共N行,每行M-1个数,表示横向道路的权值. 

第二部分共N-1行,每行M个数,表示纵向道路的权值. 

第三部分共N-1行,每行M-1个数,表示斜向道路的权值. 

输入文件保证不超过10M

Output

输出一个整数,表示参与伏击的狼的最小数量.

Sample Input

3 4 5 6 4 4 3 1 7 5 3 5 6 7 8 8 7 6 5 5 5 5 6 6 6

Sample Output

14

按照题意,很容易想到这是个求最小割的题目,但是有一个点数和边数都高达1e6,1e6,因此直接跑dinic肯定是不行的。因此就需要把原图转化为对偶图。

下面介绍一下对偶图:

给出一张连通图G

图就会被分成几个封闭区域,我们记为A,B,C

将点与点之间连边,转化为这条边所属的两个区域之间连边,对偶图中的边的权重和原图中的那条边一样,如一条边只属于某一个区域,那么就由这个区域向它自己连一条边。那么原图就会被转化为这样

我们把原图G的对偶图记为G*

G*具有性质:G*中的环对应G中的割

那么应用这个性质我们就可以通过图转对偶图来使用最短路求最小割了。

具体方案如下:

我们在将要求最小割的两个点之间连上一条边权无限大的边,然后将原图转对偶图,对偶图中点的标号如下(边均未标出)

然后从跑13,14之间的最短路即是从start到end的最大流(最小割)

AC代码:

#include<iostream>
#include<queue>
#include<cstdio> 
#include<cstring>
using namespace std;
const int size=2e6+5;//the number of point  
const int inf=0x3f3f3f3f;
struct Edge{
	int u,v,w;
	Edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){} 
}; 
struct node{
	int id,w;
	node(int id=0,int w=0):id(id),w(w){}
	friend bool operator<(node a,node b)
	{
		return a.w>b.w;
	} 
};
priority_queue<node> q;
vector<Edge> edge[size];
int dis[size],vis[size];
void Dijkstra(int beg)
{
	memset(dis,inf,sizeof(dis));
	memset(vis,0,sizeof(vis));
	while(!q.empty()) q.pop();
	q.push(node(beg,0)),dis[beg]=0;
	while(!q.empty())
	{
		node s=q.top();
		q.pop();
		int id=s.id;
		if(dis[id]!=s.w) continue;
		for(int i=0;i<edge[id].size();i++)
		{
			if(dis[edge[id][i].v]>dis[id]+edge[id][i].w)
			{
				dis[edge[id][i].v]=dis[id]+edge[id][i].w;
				q.push(node(edge[id][i].v,dis[edge[id][i].v]));
			}
		}
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	int ss=2*(n-1)*(m-1)+1,tt=2*(n-1)*(m-1)+2;
	int w;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m-1;j++)
		{
			scanf("%d",&w);
			if(i==1) edge[ss].push_back(Edge(ss,j,w)),edge[j].push_back(Edge(j,ss,w));
			else if(i!=n)
			{
				int u=(i-1)*(m-1)*2+j,v=(i-1)*(m-1)*2+j-m+1;
				edge[u].push_back(Edge(u,v,w));
				edge[v].push_back(Edge(v,u,w));
			}
			else
			{
				int v=(i-1)*(m-1)*2+j-m+1;
				edge[v].push_back(Edge(v,tt,w));
				edge[tt].push_back(Edge(tt,v,w));
			}
		}
	} 
	for(int i=1;i<=n-1;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&w);
			if(j==1)
			{
				edge[tt].push_back(Edge(tt,2*(i-1)*(m-1)+(m-1)+j,w));
				edge[2*(i-1)*(m-1)+(m-1)+j].push_back(Edge(2*(i-1)*(m-1)+(m-1)+j,tt,w));
			}
			else if(j!=m){
				int u=2*(i-1)*(m-1)+j-1,v=2*(i-1)*(m-1)+(m-1)+j;
				edge[u].push_back(Edge(u,v,w));
				edge[v].push_back(Edge(v,u,w));
			}else
			{
				int u=2*(i-1)*(m-1)+j-1;
				edge[u].push_back(Edge(u,ss,w));
				edge[ss].push_back(Edge(ss,u,w));
			}
		}
	}
	for(int i=1;i<=n-1;i++)
	{
		for(int j=1;j<=m-1;j++)
		{
			scanf("%d",&w);
			int u=2*(i-1)*(m-1)+j,v=2*(i-1)*(m-1)+m-1+j;
			edge[u].push_back(Edge(u,v,w));
			edge[v].push_back(Edge(v,u,w));
		}
	}
	Dijkstra(ss);
	printf("%d\n",dis[tt]);
}

猜你喜欢

转载自blog.csdn.net/baiyifeifei/article/details/83024818