• 【bzoj2668】[cqoi2012]交换棋子 费用流


    题目描述

    有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

    输入

    第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。

    输出

    输出仅一行,为最小交换总次数。如果无解,输出-1。

    样例输入

    3 3
    110
    000
    001
    000
    110
    100
    222
    222
    222

    样例输出

    4


    题解

    费用流

    设 $a_{i,j}=[(i,j)原来为白色]$ ,$b_{i,j}=[(i,j)后来为白色]$ 。

    显然可以只考虑白点的移动,转化为费用流问题:
     $S$ 向 $a_{i,j}=1$ 的点连边,容量为1,费用为0;$b_{i,j}=1$ 的点向T连边,容量为1,费用为0;相邻的点之间互相连边,容量为inf,费用为1。最小费用最大流即为答案。

    但是这样有一个问题:给定的度数限制是入度+出度的限制,而不是分别的限制,这样无法直接拆点限制度数。

    考虑,对于一个点,除去可能存在的 $S$ 到其的入边 及 其到 $T$ 的出边 以外,其它的一定入度=出度。因此入度就是 $lfloorfrac{m_{i,j}-a_{i,j}-b_{i,j}}2 floor+b_{i,j}=lfloorfrac{m_{i,j}-a_{i,j}+b_{i,j}}2 floor$ ,出度同理。

    这样就能够求出一个点具体的入度出度限制,把一个点拆成3个:入点、中间点和出点。入点向中间点连边,容量为入度;中间点向出点连边,容量为出度。这样就限制了度数。

    时间复杂度 $O(费用流)$ 

    注意题目中的“连通”指的是八连通(一开始当成四连通卡了好久。。。)

    #include <queue>
    #include <cstdio>
    #include <cstring>
    #define N 1210
    #define M 121000
    #define inf 1 << 30
    #define pos(i , j , k) (k * n * m + (i - 1) * m + j)
    using namespace std;
    queue<int> q;
    int a[25][25] , b[25][25] , c[25][25] , head[N] , to[M] , val[M] , cost[M] , next[M] , cnt = 1 , s , t , dis[N] , from[N] , pre[N];
    inline 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];
    }
    inline int rnum()
    {
    	char ch = getchar();
    	while(ch < '0' || ch > '9') ch = getchar();
    	return ch ^ '0';
    }
    int main()
    {
    	int n , m , i , j , sum1 = 0 , sum2 = 0 , ans = 0;
    	scanf("%d%d" , &n , &m) , s = 0 , t = 3 * n * m + 1;
    	for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= m ; j ++ ) a[i][j] = rnum();
    	for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= m ; j ++ ) b[i][j] = rnum();
    	for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= m ; j ++ ) c[i][j] = rnum();
    	for(i = 1 ; i <= n ; i ++ )
    	{
    		for(j = 1 ; j <= m ; j ++ )
    		{
    			if(!c[i][j] && a[i][j] != b[i][j])
    			{
    				puts("-1");
    				return 0;
    			}
    			add(pos(i , j , 0) , pos(i , j , 1) , (c[i][j] - a[i][j] + b[i][j]) >> 1 , 0);
    			add(pos(i , j , 1) , pos(i , j , 2) , (c[i][j] + a[i][j] - b[i][j]) >> 1 , 0);
    			if(a[i][j]) add(s , pos(i , j , 1) , 1 , 0) , sum1 ++ ;
    			if(b[i][j]) add(pos(i , j , 1) , t , 1 , 0) , sum2 ++ ;
    			if(i > 1) add(pos(i , j , 2) , pos(i - 1 , j , 0) , inf , 1);
    			if(i < n) add(pos(i , j , 2) , pos(i + 1 , j , 0) , inf , 1);
    			if(j > 1) add(pos(i , j , 2) , pos(i , j - 1 , 0) , inf , 1);
    			if(j < m) add(pos(i , j , 2) , pos(i , j + 1 , 0) , inf , 1);
    			if(i > 1 && j > 1) add(pos(i , j , 2) , pos(i - 1 , j - 1 , 0) , inf , 1);
    			if(i > 1 && j < m) add(pos(i , j , 2) , pos(i - 1 , j + 1 , 0) , inf , 1);
    			if(i < n && j > 1) add(pos(i , j , 2) , pos(i + 1 , j - 1 , 0) , inf , 1);
    			if(i < n && j < m) add(pos(i , j , 2) , pos(i + 1 , j + 1 , 0) , inf , 1);
    		}
    	}
    	if(sum1 != sum2) puts("-1");
    	else
    	{
    		while(spfa())
    		{
    			j = inf;
    			for(i = t ; i != s ; i = from[i]) j = min(j , val[pre[i]]);
    			sum1 -= j , ans += j * dis[t];
    			for(i = t ; i != s ; i = from[i]) val[pre[i]] -= j , val[pre[i] ^ 1] += j;
    		}
    		if(sum1) puts("-1");
    		else printf("%d
    " , ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    SqL读取XML、解析XML、SqL将XML转换DataTable、SqL将XML转换表
    C#DataTable复制、C#DataTable列复制、C#DataTable字段复制
    Dev、GridControl的模糊查询
    C#两个时间相减
    C# % 和 /
    C#时间、日期 的操作
    linux文件操作
    shell ftp上传下载文件
    POM详细配置
    maven 本地setting.xml配置
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8310945.html
Copyright © 2020-2023  润新知