【bzoj4930】棋盤 費用流
阿新 • • 發佈:2017-07-03
family can 存在 bool cstring ext 一個 提高 front
輸出共q行,每行代表對應詢問的最少的互相能攻擊到的棋子對數。
題目描述
給定一個n×n的棋盤,棋盤上每個位置要麽為空要麽為障礙。定義棋盤上兩個位置(x,y),(u,v)能互相攻擊當前僅 當滿足以下兩個條件: 1:x=u或y=v 2:對於(x,y)與(u,v)之間的所有位置,均不是障礙。 現在有q個詢問,每個詢問給定ki,要求從棋盤中選出ki個空位置來放棋子,問最少互相能攻擊到的棋子對數是多少?輸入
第一行一個整數n。 接下來輸入一個n×n的字符矩陣,一個位置若為.,則表示這是一個空位置,若為#,則為障礙。 第n+2行輸入一個整數q代表詢問個數。 接下來q行,每行一個整數k,代表要放的棋子個數。 n ≤ 50, q ≤ 10000, k ≤ 棋盤中空位置數量輸出
樣例輸入
4
..#.
####
..#.
..#.
1
7
樣例輸出
2
題解
費用流, bzoj4554 的強化版
按照那道題的思路,把相互影響的行和列的部分拿出來,同一個點的行部分和列部分之間連邊。
不過這道題是固定棋子數,問最小的影響的棋子對數。
考慮,一個行或列的部分,如果存在k個棋子,那麽相互影響的棋子對數為$\frac{k(k-1)}2$對(兩個棋子之間隔著其它棋子也算相互影響)。
所以我們可以使用拆邊法來解決,從S到行的部分、從列的部分到T連d條邊,其中d為該部分的位置數。第i條邊的費用為$\frac{i(i-1)}2-\frac{(i-1)(i-2)}2=i-1$。
然後跑費用流。在此過程中,由於每條增廣路的容量必定為1,所以相當於每次多放置了一個棋子。這樣我們可以只跑一次EK費用流即可預處理出所有答案,然後再$O(1)$回答。
時間有點長但可以過,可以動態加邊來提高效率(這裏懶了沒有寫)
#include <cstdio> #include <cstring> #include <queue> #define N 6010 #define M 1200010 #define inf 0x3f3f3f3f using namespace std; queue<int> q; int map[60][60] , bx[60][60] , tx , by[60][60] , ty , sx[N] , sy[N] , head[N] , to[M] , val[M] , cost[M] , next[M] , cnt = 1 , s , t , dis[N] , from[N] , pre[N] , ans[N]; char str[60]; void add(int x , int y , int v , int c) { to[++cnt] = y , val[cnt] = v , cost[cnt] = c , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , cost[cnt] = -c , next[cnt] = head[y] , head[y] = cnt; } bool spfa() { int x , i; memset(from , -1 , sizeof(from)); memset(dis, 0x3f , sizeof(dis)); dis[s] = 0 , 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[x] + cost[i]) dis[to[i]] = dis[x] + cost[i] , from[to[i]] = x , pre[to[i]] = i , q.push(to[i]); } return ~from[t]; } void mincost() { int k = 0 , i; while(spfa()) { k ++ , ans[k] = ans[k - 1] + dis[t]; for(i = t ; i != s ; i = from[i]) val[pre[i]] -- , val[pre[i] ^ 1] ++ ; } } int main() { int n , q , i , j , x; scanf("%d" , &n); for(i = 1 ; i <= n ; i ++ ) { scanf("%s" , str + 1); for(j = 1 ; j <= n ; j ++ ) map[i][j] = (str[j] == ‘#‘); } for(i = 1 ; i <= n ; i ++ ) { tx ++ ; for(j = 1 ; j <= n ; j ++ ) bx[i][j] = tx , sx[tx] ++ , tx += map[i][j]; } for(j = 1 ; j <= n ; j ++ ) { ty ++ ; for(i = 1 ; i <= n ; i ++ ) by[i][j] = ty , sy[ty] ++ , ty += map[i][j]; } s = 0 , t = tx + ty + 1; for(i = 1 ; i <= tx ; i ++ ) for(j = 0 ; j < sx[i] ; j ++ ) add(s , i , 1 , j); for(i = 1 ; i <= ty ; i ++ ) for(j = 0 ; j < sy[i] ; j ++ ) add(i + tx , t , 1 , j); for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= n ; j ++ ) if(!map[i][j]) add(bx[i][j] , by[i][j] + tx , 1 , 0); mincost(); scanf("%d" , &q); while(q -- ) scanf("%d" , &x) , printf("%d\n" , ans[x]); return 0; }
【bzoj4930】棋盤 費用流