Processing math: 100%

【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


题解

费用流

ai,j=[(i,j)]bi,j=[(i,j)]

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

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

考虑,对于一个点,除去可能存在的 S 到其的入边 及 其到 T 的出边 以外,其它的一定入度=出度。因此入度就是 mi,jai,jbi,j2+bi,j=mi,jai,j+bi,j2 ,出度同理。

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

时间复杂度 O() 

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#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\n" , ans);
    }
    return 0;
}

 

posted @   GXZlegend  阅读(444)  评论(0编辑  收藏  举报
编辑推荐:
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
阅读排行:
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥
历史上的今天:
2017-01-18 【poj2104】K-th Number 主席树
2017-01-18 【bzoj3772】精神污染 STL+LCA+主席树
2017-01-18 【bzoj3932】[CQOI2015]任务查询系统 离散化+主席树
点击右上角即可分享
微信分享提示