标签:存在 val 字符 cst 费用流 can 矩阵 棋盘 提高
题目描述
输入
输出
样例输入
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; }
标签:存在 val 字符 cst 费用流 can 矩阵 棋盘 提高
原文地址:http://www.cnblogs.com/GXZlegend/p/7112612.html