1. 程式人生 > >【bzoj2150】部落戰爭 有上下界最小流

【bzoj2150】部落戰爭 有上下界最小流

獲得 ace 描述 其中 map 刪除 light ini 流量

題目描述

lanzerb的部落在A國的上部,他們不滿天寒地凍的環境,於是準備向A國的下部征戰來獲得更大的領土。 A國是一個M*N的矩陣,其中某些地方是城鎮,某些地方是高山深澗無人居住。lanzerb把自己的部落分成若幹支軍隊,他們約定: 1. 每支軍隊可以從任意一個城鎮出發,並只能從上往向下征戰,不能回頭。途中只能經過城鎮,不能經過高山深澗。 2. 如果某個城鎮被某支軍隊到過,則其他軍隊不能再去那個城鎮了。 3. 每支軍隊都可以在任意一個城鎮停止征戰。 4. 所有軍隊都很奇怪,他們走的方法有點像國際象棋中的馬。不過馬每次只能走1*2的路線,而他們只能走R*C的路線。 lanzerb的野心使得他的目標是統一全國,但是兵力的限制使得他們在配備人手時力不從心。假設他們每支軍隊都能順利占領這支軍隊經過的所有城鎮,請你幫lanzerb算算至少要多少支軍隊才能完成統一全國的大業。

輸入

第一行包含4個整數M、N、R、C,意義見問題描述。接下來M行每行一個長度為N的字符串。如果某個字符是‘.‘,表示這個地方是城鎮;如果這個字符時‘x‘,表示這個地方是高山深澗。

輸出

輸出一個整數,表示最少的軍隊個數。

樣例輸入

【樣例輸入一】
3 3 1 2
...
.x.
...
【樣例輸入二】
5 4 1 1
....
..x.
...x
....
x...

樣例輸出

【樣例輸出一】
4
【樣例輸出二】
5


題解

很容易看出來的有上下界最小流

將每個點拆成兩個,設為xi和yi。S(代碼中的b)->xi,容量為1,yi->T,容量為1,xi->yi,容量下界為1,上界為1。

若點i能夠到達點j,則yi->xj,容量為1。

這個圖的最小流即為答案。

再具體一些的最小流轉化為最大流的方法:

S->xi,容量為1,yi->T,容量為1;若點i能夠到達點j,則yi->xj,容量為1;

建立超級源匯SS和TT(代碼中的s和t),SS->yi,容量為1,xi->TT,容量為1;

T->S,容量為inf。

從SS到TT跑最大流,記錄T->S的邊的流量(即反向邊的殘量)為ans1。

然後刪除所有與SS或TT相連的邊,刪除T->S的邊,從T到S跑最大流,記為ans2。

ans1-ans2即為答案。

註意往下走是行數增加而不是列數增加,不要弄混。

#include <cstdio>
#include <cstring>
#include <queue>
#define N 6000
#define M 200000
#define inf 0x7fffffff
#define pos(i , j , k) k * n * m + (i - 1) * m + j
using namespace std;
queue<int> q;
int map[60][60] , head[N] , to[M] , val[M] , next[M] , cnt = 1 , s , t , dis[N];
char str[60];
void add(int x , int y , int z)
{
	to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
	to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
	int x , i;
	memset(dis , 0 , sizeof(dis));
	while(!q.empty()) q.pop();
	dis[s] = 1 , q.push(s);
	while(!q.empty())
	{
		x = q.front() , q.pop();
		for(i = head[x] ; i ; i = next[i])
		{
			if(val[i] && !dis[to[i]])
			{
				dis[to[i]] = dis[x] + 1;
				if(to[i] == t) return 1;
				q.push(to[i]);
			}
		}
	}
	return 0;
}
int dinic(int x , int low)
{
	if(x == t) return low;
	int temp = low , i , k;
	for(i = head[x] ; i ; i = next[i])
	{
		if(val[i] && dis[to[i]] == dis[x] + 1)
		{
			k = dinic(to[i] , min(temp , val[i]));
			if(!k) dis[to[i]] = 0;
			val[i] -= k , val[i ^ 1] += k;
			if(!(temp -= k)) break;
		}
	}
	return low - temp;
}
int main()
{
	int n , m , r , c , i , j , b , e , ans = 0;
	scanf("%d%d%d%d" , &n , &m , &r , &c) , b = 0 , e = 2 * n * m + 1 , s = 2 * n * m + 2 , t = 2 * n * m + 3;
	add(e , b , inf);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%s" , str + 1);
		for(j = 1 ; j <= m ; j ++ ) map[i][j] = (str[j] == ‘.‘);
	}
	for(i = 1 ; i <= n ; i ++ )
	{
		for(j = 1 ; j <= m ; j ++ )
		{
			if(map[i][j])
			{
				add(b , pos(i , j , 0) , 1) , add(pos(i , j , 1) , e , 1) , add(s , pos(i , j , 1) , 1) , add(pos(i , j , 0) , t , 1);
				if(i + r <= n && j + c <= m && map[i + r][j + c]) add(pos(i , j , 1) , pos(i + r , j + c , 0) , 1);
				if(i + r <= n && j - c >= 1 && map[i + r][j - c]) add(pos(i , j , 1) , pos(i + r , j - c , 0) , 1);
				if(i + c <= n && j + r <= m && map[i + c][j + r]) add(pos(i , j , 1) , pos(i + c , j + r , 0) , 1);
				if(i + c <= n && j - r >= 1 && map[i + c][j - r]) add(pos(i , j , 1) , pos(i + c , j - r , 0) , 1);
			}
		}
	}
	while(bfs()) dinic(s , inf);
	ans = val[3];
	for(i = head[s] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
	for(i = head[t] ; i ; i = next[i]) val[i] = val[i ^ 1] = 0;
	val[2] = val[3] = 0 , s = e , t = b;
	while(bfs()) ans -= dinic(s , inf);
	printf("%d\n" , ans);
	return 0;
}

【bzoj2150】部落戰爭 有上下界最小流