【HDU】3870 Catch the Theves 对偶图全局最小割 最短路

Catch the Theves

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65768/32768 K (Java/Others)
Total Submission(s): 1421    Accepted Submission(s): 431


Problem Description
A group of thieves is approaching a museum in the country of zjsxzy,now they are in city A,and the museum is in city B,where keeps many broken legs of zjsxzy.Luckily,GW learned the conspiracy when he is watching stars and told it to zjsxzy.
Zjsxzy decided to caught these thieves,and he let the police to do this,the police try to catch them on their way from A to B. Although the thieves might travel this way by more than one group, zjsxzy's excellent police has already gather the statistics that the cost needed on each road to guard it.
Now ,zjsxzy's conutry can be described as a N*N matrix A,Aij indicates the city(i,j) have bidirectionals road to city(i+1,j) and city(i,j+1),gurad anyone of them costs Aij.
Now give you the map,help zjsxzy to calculate the minimium cost.We assume thieves may travel in any way,and we will catch all passing thieves on a road if we guard it.
 

Input
The first line is an integer T,followed by T test cases.
In each test case,the first line contains a number N(1<N<=400).
The following N lines,each line is N numbers,the jth number of the ith line is Aij.
The city A is always located on (1,1) and the city B is always located on (n,n).
Of course,the city (i,j) at the last row or last line won't have road to (i,j+1) or (i+1,j).
 

Output
For each case,print a line with a number indicating the minimium cost to arrest all thieves.
 

Sample Input
  
  
1 3 10 5 5 6 6 20 4 7 9
 

Sample Output
  
  
18
Hint
The map is like this:
      
      

 

Source
2011 Multi-University Training Contest 4 - Host by SDU

传送门:【HDU】3870 Catch the Theves

题目大意:现在有一群小偷要从最左上角逃到最右下角,小偷很聪明,每到一个分岔口他们可以分成两拨,现在你是警察,你要阻止他们任何一个逃离到最右下角,已知封锁每条道路的费用,问最少需要花费多少可以达到目的?

题目分析:按照体面理解,就是要我们求全局最小割,但是,网络流不超时几乎是不可能的,16万的点,想想也不太现实。。。。
但是图很特殊,是对偶图,那么我们可以构造这样的一副新图:
新建源汇点,然后平面图的每个面当成一个点,相邻两点间建无向边,边权为两点重叠的边的边权。然后源点S向最左面与最下面的点建边,边权为最左面与最下面的路径的花费,同理汇点T与最上面与最右面建边。
易知每一条S--T的路都是一条割,那么S--T的最短路就是需要求的最小割。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clear( a , x ) memset ( a , x , sizeof a )

const int MAXN = 170000 ;
const int MAXE = 700000 ;
const int MAXH = 700000 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , c , n ;
	Edge ( int var = 0 , int cost = 0 , int next = 0 ) :
		v ( var ) , c ( cost ) , n ( next ) {}
} ;

struct Heap {
	int w , idx ;
	Heap ( int _w = 0 , int _idx = 0 ) :
		w ( _w ) , idx ( _idx ) {}
} ;

struct priority_queue {
	Heap heap[MAXH] ;
	int top ;
	
	void init () {
		top = 1 ;
	}
	
	int cmp ( const Heap &a , const Heap &b ) {
		return a.w < b.w ;
	}
	
	void push ( int w , int idx ) {
		heap[top] = Heap ( w , idx ) ;
		int o = top ++ ;
		while ( o > 1 && cmp ( heap[o] , heap[o >> 1] ) )
			swap ( heap[o] , heap[o >> 1] ) , o >>= 1 ;
	}
	
	int front () {
		return heap[1].idx ;
	}
	
	int empty () {
		return top == 1 ;
	}
	
	void pop () {
		heap[1] = heap[-- top] ;
		int o = 1 , p = o , l = o << 1 , r = o << 1 | 1 ;
		while ( o < top ) {
			if ( l < top && cmp ( heap[l] , heap[p] ) )
				p = l ;
			if ( r < top && cmp ( heap[r] , heap[p] ) )
				p = r ;
			if ( p == o )
				break ;
			swap ( heap[o] , heap[p] ) ;
			o = p , l = o << 1 , r = o << 1 | 1 ;
		}
	}
} ;

struct Dij {
	priority_queue q ;
	Edge edge[MAXE] ;
	int adj[MAXN] , cntE ;
	int d[MAXN] ;
	bool done[MAXN] ;
	
	void init () {
		cntE = 0 ;
		clear ( adj , -1 ) ;
	}
	
	void addedge ( int u , int v , int c ) {
		edge[cntE] = Edge ( v , c , adj[u] ) ;
		adj[u] = cntE ++ ;
		edge[cntE] = Edge ( u , c , adj[v] ) ;
		adj[v] = cntE ++ ;
	}
	
	void dijkstra ( int s , int t ) {
		q.init () ;
		clear ( d , INF ) ;
		clear ( done , 0 ) ;
		d[s] = 0 ;
		q.push ( d[s] , s ) ;
		while ( !q.empty () ) {
			int u = q.front () ;
			q.pop () ;
			if ( done[u] )
				continue ;
			done[u] = 1 ;
			for ( int i = adj[u] ; ~i ; i = edge[i].n ) {
				int v = edge[i].v , c = edge[i].c ;
				if ( d[v] > d[u] + c ) {
					d[v] = d[u] + c ;
					q.push ( d[v] , v ) ;
				}
			}
		}
	}
} ;

Dij z ;

void work () {
	int n , m ;
	int s , t ;
	int x ;
	z.init () ;
	scanf ( "%d" , &n ) ;
	m = n - 1 ;
	s = m * m ;
	t = m * m + 1 ;
	REP ( i , n )
		REP ( j , n ) {
			scanf ( "%d" , &x ) ;
			if ( i < m ) {
				if ( j == 0 )
					z.addedge ( s , i * m + j , x ) ;
				else if ( j < m )
					z.addedge ( i * m + j - 1 , i * m + j , x ) ;
				else if ( j == m )
					z.addedge ( i * m + j - 1 , t , x ) ;
			}
			if ( j < m ) {
				if ( i == 0 )
					z.addedge ( i * m + j , t , x ) ;
				else if ( i < m )
					z.addedge ( i * m + j , i * m + j - m , x ) ;
				else if ( i == m )
					z.addedge ( s , i * m + j - m , x ) ;
			}
		}
	z.dijkstra ( s , t ) ;
	printf ( "%d\n" , z.d[t] ) ;
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- )
		work () ;
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值