1. 程式人生 > >1001 (平面圖轉對偶圖最短路求最小割)

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