题目描述
有一个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
分析:
我们只考虑黑色棋子。交换两个相同颜色的棋子没有影响,交换不同颜色的棋子相当于移动黑色的棋子。可以知道,一个棋子从
移动到
,只有起点和终点的格子进行一次交换,而其他点进行了两次交换。
我们把一个点拆成三个,分别为
。
表示一条从
到
的流量为
,费用为
的边。
对于一条连接
连向
的边,表示一条从
连出去的路径;一条
向
,表示一条走向
的路径。
如果某个点原来是白色,后来变为黑色,则这个点是某一条路径的终点,也就是有一次只进行一次交换。
连一条
和一条
的边。
同理,原理是黑色的,后来变为白色的,
连一条
和一条
的边。
相同颜色的,
连一条
和一条
的边。
对于原图每个黑点,连一条
的边;对于后面的每个黑点,连一条
的边;对于相邻的两个点
连一条
的边。跑出来的费用就是每个点出边数和入边数的总和,由于每一次交换包含一条入边和一条出边,给这个除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);
}