1. 程式人生 > >【bzoj4930】棋盤 費用流

【bzoj4930】棋盤 費用流

family can 存在 bool cstring ext 一個 提高 front

題目描述

給定一個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 ≤ 棋盤中空位置數量

輸出

輸出共q行,每行代表對應詢問的最少的互相能攻擊到的棋子對數。

樣例輸入

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】棋盤 費用流