Description
有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
Input
第一行包含两个整数n,m(1<=n, m<=20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m个0~9数字的字符串,表示每个格子参与交换的次数上限。
Output
输出仅一行,为最小交换总次数。如果无解,输出-1。
Sample Input
3 3
110
000
001
000
110
100
222
222
222
110
000
001
000
110
100
222
222
222
Sample Output
4
题解:
我们只需要把黑色棋子移动到目标位置即可.
考虑一条合法的移动路径.
开始和结尾的点只交换了一次,中间的点交换了两次.
所以我们可以把每个点拆成三个点(a,b,c).
如果这个点是原图中的黑点.
a向b连流量为c[i][j]/2,费用为0的边.
b向c连流量为(c[i][j]+1)/2,费用为0的边.
S向b连流量为1,费用为0的边.
如果这个点事新图中的黑点.
a向b连流量为(c[i][j]+1)/2,费用为0的边.
b向c连流量为c[i][j]/2,费用为0的边.
b向T连流量为1,费用为0的边.
如果这个点在新图和原图中都是白点.
a向b连流量为c[i][j]/2,费用为0的边.
b向c连流量为c[i][j]/2,费用为0的边.
对于相邻的两个点(x,y)
x.c向y.a连流量为inf,费用为1的边.
跑最小费用最大流即可.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2000
#define M 50000
#define inf 707406378
using namespace std;
char map[40][40],p[50][50],s[50][50];
int n,m,T,dis[N],f[N],q[N*200],pre[N],c[50][50],sum1,sum2,ans;
int cnt(1),next[M<<1],point[N],num,ff,a[50][50],b[50][50];
int x[8]={1,0,-1,0,1,1,-1,-1},y[8]={0,1,0,-1,-1,1,-1,1};
struct use{
int st,en,v,c;
}e[M<<1];
void add(int x,int y,int v,int c){
next[++cnt]=point[x];point[x]=cnt;
e[cnt].st=x;e[cnt].en=y;e[cnt].v=v;e[cnt].c=c;
next[++cnt]=point[y];point[y]=cnt;
e[cnt].st=y;e[cnt].en=x;e[cnt].v=0;e[cnt].c=-c;
}
bool spfa(){
int h(0),t(1);
memset(dis,127/3,sizeof(dis));
memset(f,0,sizeof(f));
f[1]=1;dis[1]=0;q[t]=1;
while (h<t){
int u=q[++h];f[u]=0;
for (int i=point[u];i;i=next[i])
if (e[i].v&&dis[e[i].en]>dis[u]+e[i].c){
pre[e[i].en]=i;dis[e[i].en]=dis[u]+e[i].c;
if (!f[e[i].en]){
f[e[i].en]=1;
q[++t]=e[i].en;
}
}
}
return dis[T]!=inf;
}
void isap(){
int mn=inf;
for (int i=T;i!=1;i=e[pre[i]].st) mn=min(mn,e[pre[i]].v);
num+=mn;
for (int i=T;i!=1;i=e[pre[i]].st){
e[pre[i]].v-=mn;e[pre[i]^1].v+=mn;ans+=mn*e[pre[i]].c;
}
}
int cal(int x,int y){return (x-1)*m+y-1;}
int main(){
scanf("%d%d",&n,&m);T=n*m*3+2;
for (int i=1;i<=n;i++) scanf("%s",map[i]+1);
for (int i=1;i<=n;i++) scanf("%s",p[i]+1);
for (int i=1;i<=n;i++) scanf("%s",s[i]+1);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
c[i][j]=s[i][j]-'0';
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
a[i][j]=map[i][j]-'0';
b[i][j]=p[i][j]-'0';
if (!a[i][j]) sum1++;
if (!b[i][j]) sum2++;
if ((!a[i][j])&&(!b[i][j])) sum1--,sum2--,a[i][j]=b[i][j]=1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
if (!a[i][j]){
add(cal(i,j)*3+3,cal(i,j)*3+2,(c[i][j])/2,0);
add(cal(i,j)*3+2,cal(i,j)*3+4,(c[i][j]+1)/2,0);
add(1,cal(i,j)*3+2,1,0);
}
if (!b[i][j]){
add(cal(i,j)*3+3,cal(i,j)*3+2,(c[i][j]+1)/2,0);
add(cal(i,j)*3+2,cal(i,j)*3+4,(c[i][j])/2,0);
add(cal(i,j)*3+2,T,1,0);
}
if (a[i][j]&&b[i][j]){
add(cal(i,j)*3+3,cal(i,j)*3+2,(c[i][j])/2,0);
add(cal(i,j)*3+2,cal(i,j)*3+4,(c[i][j])/2,0);
}
}
if (sum1!=sum2){return cout<<-1<<endl,0;}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
for (int k=0;k<=7;k++){
int xx=i+x[k],yy=j+y[k];
if (xx>n||xx<1||yy>m||yy<1) continue;
add(cal(i,j)*3+4,cal(xx,yy)*3+3,inf,1);
}
}
while (spfa()) isap();
if (num!=sum1){return cout<<-1<<endl,0;}
cout<<ans<<endl;
}