洛谷 P3159 [CQOI2012]交换棋子 费用流

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82285953

题目描述

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

输入输出格式

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

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

输入输出样例

输入样例#1:
3 3
110
000
001
000
110
100
222
222
222
输出样例#1:
4

分析:
我们只考虑黑色棋子。交换两个相同颜色的棋子没有影响,交换不同颜色的棋子相当于移动黑色的棋子。可以知道,一个棋子从 i 移动到 j ,只有起点和终点的格子进行一次交换,而其他点进行了两次交换。
我们把一个点拆成三个,分别为 l e f t , n o w , r i g h t
( u , v , w , c ) 表示一条从 u v 的流量为 w ,费用为 c 的边。
对于一条连接 n o w ( i ) 连向 r i g h t ( i ) 的边,表示一条从 i 连出去的路径;一条 l e f t ( i ) n o w ( i ) ,表示一条走向 i 的路径。

如果某个点原来是白色,后来变为黑色,则这个点是某一条路径的终点,也就是有一次只进行一次交换。
连一条 ( l e f t , n o w , ( k + 1 ) / 2 , 1 ) 和一条 ( n o w , r i g h t , k / 2 , 1 ) 的边。
同理,原理是黑色的,后来变为白色的,
连一条 ( l e f t , n o w , k / 2 , 1 ) 和一条 ( n o w , r i g h t , ( k + 1 ) / 2 , 1 ) 的边。
相同颜色的,
连一条 ( l e f t , n o w , k / 2 , 1 ) 和一条 ( n o w , r i g h t , k / 2 , 1 ) 的边。
对于原图每个黑点,连一条 ( s , n o w , 1 , 0 ) 的边;对于后面的每个黑点,连一条 ( n o w , t , 1 , 0 ) 的边;对于相邻的两个点 ( i , j ) 连一条 ( r i g h t ( i ) , l e f t ( j ) , i n f , 0 ) 的边。跑出来的费用就是每个点出边数和入边数的总和,由于每一次交换包含一条入边和一条出边,给这个除2就是答案。
如果前后黑点数不同或者最大流量小于黑点数,则无解。

代码:

// luogu-judger-enable-o2
#include <iostream>
#include <cmath>
#include <cstdio>
#include <queue>

const int maxn=2007;
const int maxe=1e5+7;
const int dx[8]={0,0,-1,1,1,1,-1,-1};
const int dy[8]={1,-1,0,0,1,-1,1,-1};
const int inf=0x3f3f3f3f;

using namespace std;

int n,m,sum,s,t,ans,cnt,maxflow,sum1,sum2;
int ls[maxn],dis[maxn],pre[maxn],vis[maxn];
int a[30][30],b[30][30];
char ch[107];

struct edge{
    int x,y,w,c,op,next;
}g[maxe];

queue <int> q;

int po(int x,int y)
{
    return (x-1)*m+y;
}

void add(int x,int y,int w,int c)
{
    g[++cnt]=(edge){x,y,w,c,cnt+1,ls[x]};
    ls[x]=cnt;
    g[++cnt]=(edge){y,x,0,-c,cnt-1,ls[y]};
    ls[y]=cnt;
}

bool spfa()
{
    for (int i=s;i<=t;i++) dis[i]=inf;
    q.push(s);
    dis[s]=0;
    vis[s]=1;
    while (!q.empty())
    {
        int x=q.front();
        q.pop();
        for (int i=ls[x];i>0;i=g[i].next)
        {
            int y=g[i].y;
            if ((g[i].w) && (dis[y]>dis[x]+g[i].c))
            {
                dis[y]=dis[x]+g[i].c;
                pre[y]=i;
                if (!vis[y])
                {
                    vis[y]=1;
                    q.push(y);
                }
            }
        }
        vis[x]=0;
    }
    return (dis[t]!=inf);
}

void mcf()
{
    int flow=inf,i=t;
    while (pre[i])
    {
        flow=min(flow,g[pre[i]].w);
        i=g[pre[i]].x;
    }
    maxflow+=flow;
    i=t;
    while (pre[i])
    {
        g[pre[i]].w-=flow;
        g[g[pre[i]].op].w+=flow;
        ans+=g[pre[i]].c*flow;
        i=g[pre[i]].x;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    s=0,t=n*m*3+1;
    for (int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        for (int j=1;j<=m;j++)
        {
            a[i][j]=(ch[j-1]=='1');
            if (ch[j-1]=='1') add(s,n*m+po(i,j),1,0),sum1++;
        }
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        for (int j=1;j<=m;j++)
        {
            b[i][j]=(ch[j-1]=='1');
            if (ch[j-1]=='1') add(n*m+po(i,j),t,1,0),sum2++;
        }
    }
    for (int i=1;i<=n;i++)
    {
        scanf("%s",ch);
        for (int j=1;j<=m;j++)
        {
            int c=ch[j-1]-'0';
            if (a[i][j]==b[i][j])
            {
                add(po(i,j),n*m+po(i,j),c/2,1);
                add(n*m+po(i,j),n*m*2+po(i,j),c/2,1);
            }
            else
            {
                if (a[i][j]==1)
                {
                    add(po(i,j),n*m+po(i,j),c/2,1);
                    add(n*m+po(i,j),n*m*2+po(i,j),(c+1)/2,1);
                }
                else
                {
                    add(po(i,j),n*m+po(i,j),(c+1)/2,1);
                    add(n*m+po(i,j),n*m*2+po(i,j),c/2,1);
                }
            }
        }
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            for (int k=0;k<8;k++)
            {
                int x=i+dx[k],y=j+dy[k];
                if ((x>0) && (y>0) && (x<=n) && (y<=m))
                {
                    add(n*m*2+po(i,j),po(x,y),inf,0);
                }
            }
        }
    }   
    while (spfa()) mcf();
    if (maxflow!=max(sum1,sum2)) printf("-1");
                            else printf("%d",ans/2);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82285953
今日推荐