【bzoj2150】部落戰爭 有上下界最小流
題目描述
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】部落戰爭 有上下界最小流