题目描述

有一个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

思路

  对于棋盘的初始状态和最终状态可以看做二分图的两组点,而棋子的上下左右移动可以视作边的。交换黑白点可以视作讲黑子进行移动,每次费用为1,因此我们可以进行建图,但这题十分的特殊,因为它限制了每个格子的交换次数,并且用平常的拆点,我们无法表示它是否停留在改点,因此我们将1个点拆为3个点。分别为(left,now,right),每个点从left进入,停留在now上,从right出去。但对于两条边,它所限制的交换次数该如何划分又是一个问题。我们不妨把它视作黑点移动经过的次数。

  设limt表示限制数。

  对于初始和结果相同的点,边的为(l,now,limt/2,1),(now,r,limt/2,1)。

  对于初始为黑,结果为白的情况边为(l,now,limt/2,1),(now,r,(limt+1)/2,1)。

  对于初始为白,结果为黑的情况则与之相反。

原理:如果开始为黑,结果为白,则黑点一定要移动出去,则若limt为奇数时,多余的1应该加入出边。若为偶数,则加1不会对其有影响。另一种情况与之相反。

最后将源点和汇点连入图中,跑最大流最小费用即可。

代码

#include<bits/stdc++.h>
#define N 10700
#define M 107000
#define inf 1<<29
#define cnt (n*m*3)
#define left(i,j) ((i-1)*m+j)
#define now(i,j) (((i-1)*m+j)+n*m)
#define right(i,j) (((i-1)*m+j)+(n*m<<1))
using namespace std;
struct node{
    int y,z,p,next;
}e[M*2];
int tot=1,head[N],maxflow=0,ans=0;
int n,m,s,t;
void add(int x,int y,int z,int p){
    e[++tot].y=y;e[tot].z=z;e[tot].p=p;e[tot].next=head[x];head[x]=tot;
    e[++tot].y=x;e[tot].z=0;e[tot].p=-p;e[tot].next=head[y];head[y]=tot;
}
int incf[N],v[N],pre[N],d[N];
bool spfa(){
    queue<int> q;
    memset(d,0x3f,sizeof(d));// 0xcf
    memset(v,0,sizeof(v));
    q.push(s);d[s]=0;v[s]=1;
    incf[s]=inf;
    while(q.size()){
        int x=q.front();v[x]=0;q.pop();
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y,z=e[i].z;
            if(!z) continue;
            if(d[y]>d[x]+e[i].p){//d[y]<d[x]+e[i].p
                d[y]=d[x]+e[i].p;
                incf[y]=min(incf[x],z);
                pre[y]=i;
                if(!v[y]) v[y]=1,q.push(y);
            }
        }
    }
    if(d[t]==0x3f3f3f3f) return false;//0xcfcfcfcf
    return true;
}
void update(){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].z-=incf[t];
        e[i^1].z+=incf[t];
        x=e[i^1].y;
    }
    maxflow+=incf[t];
    ans+=d[t]*incf[t];
}
int x1[N][N],x2[N][N],z[N][N];
int dx[9]={0,-1,-1,-1,0,0,1,1,1};
int dy[9]={0,-1,0,1,-1,1,-1,0,1};
int main()
{
    int t1=0,t2=0,ti,tj,k;char c[30];
    cin>>n>>m;s=0;t=cnt+1;
    for(int i=1;i<=n;i++){
        scanf("%s",c);
        for(int j=1;j<=m;j++){
            if(c[j-1]=='1'){
                t1++;add(s,now(i,j),1,0);
                x1[i][j]=1;
            }
        }
    }
    for(int i=1;i<=n;i++){
        scanf("%s",c);
        for(int j=1;j<=m;j++){
            if(c[j-1]=='1'){
                t2++;add(now(i,j),t,1,0);
                x2[i][j]=1;
            }
        }
    }
    if(t1!=t2){
        cout<<"-1"<<endl;return 0;
    }
    for(int i=1;i<=n;i++){
        scanf("%s",c);
        for(int j=1;j<=m;j++){
            t2=c[j-1]-'0';
            if(x1[i][j]==x2[i][j])
                add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),t2/2,0);
            if(x1[i][j]&&!x2[i][j])
                add(left(i,j),now(i,j),t2/2,0),add(now(i,j),right(i,j),(t2+1)/2,0);
            if(!x1[i][j]&&x2[i][j])
                add(left(i,j),now(i,j),(t2+1)/2,0),add(now(i,j),right(i,j),t2/2,0);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(k=1;k<=8;k++){
                ti=i+dx[k];tj=j+dy[k];
                if(ti<1||ti>n||tj<1||tj>m) continue;
                add(right(i,j),left(ti,tj),inf,1);
            }
        }
    }

    while(spfa()) update();
    if(maxflow==t1) cout<<ans<<endl;
    else cout<<"-1"<<endl;
    return 0;
}
01-19 21:04