题目大意:有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与 M[i][j] 次交换。
题解:看做棋盘上有几个棋子,其余为空格,目标是将所有棋子移到目标位置(第一步和最后一步代价为1,其余均为2(神奇的交换))。
建立网络流模型,需要拆点,将每个格子转换为2个点,中间一条边用来限制交换次数 M[i][j] 。(A[I][j]表示拆点后的入点,B[I][j]表示拆点后的出点,{a,b}表示边的流量和费用,S,T为源汇)
S->A初始状态为1的点= {1,0}
B目标状态为1的点->T= {1,0}
A[I][j]->B[I][j]= {(M[I][j]+(是初始1)+(是目标1))/2,1}即如果不是初始或目标,此点每次移动棋子将被将还两次
B[I][j]->A八个方向= {INF,0}
最后跑出来的费用-棋子个数即为答案(每个棋子费用=棋子移动路径点数,减掉1即为交换次数)
代码:
#include
#include
#include
#define MAXN 23
using namespace std;
const int INF=0x7FFFFFFF;
namespace ValueFlow
{
const int MAX_NODE=1005,MAX_EDGE=MAX_NODE*30;
struct Edge
{
int x,w,v;
Edge *next,*back;
Edge(){}
Edge(int x,int w,int v):x(x),w(w),v(v){}
}_new[MAX_EDGE],*head[MAX_NODE],*path2[MAX_NODE];
int top,S,T,N;
int path1[MAX_NODE],dis[MAX_NODE];
bool vis[MAX_NODE];
void add_edge(int x,int y,int w,int v)
{
_new[top]=Edge(y,w,v);
_new[top].next=head[x];
_new[top].back=_new+top+1;
head[x]=_new+top;
top++;
_new[top]=Edge(x,0,-v);
_new[top].next=head[y];
_new[top].back=_new+top-1;
head[y]=_new+top;
top++;
}
void Init(int s,int t,int n)
{
top=0;
memset(head,0,sizeof head);
S=s;T=t;N=n;
}
int SPFA()
{
memset(vis,0,sizeof vis);
memset(dis,0x3F,sizeof dis);
queue<int>Q;
Q.push(S);
vis[S]=1;
dis[S]=0;
while(!Q.empty())
{
int t=Q.front();
Q.pop();vis[t]=0;
for(Edge *p=head[t];p;p=p->next)
if(p->w>0&&dis[p->x]>dis[t]+p->v)
{
dis[p->x]=dis[t]+p->v;
path1[p->x]=t;
path2[p->x]=p;
if(!vis[p->x])
{
Q.push(p->x);
vis[p->x]=1;
}
}
}
if(dis[T]==0x3F3F3F3F)
return 0;
int x=T,mn=0x7FFFFFFF;
while(x!=S)
{
mn=min(mn,path2[x]->w);
x=path1[x];
}
x=T;
while(x!=S)
{
path2[x]->w-=mn;
path2[x]->back->w+=mn;
x=path1[x];
}
return mn;
}
void getValueFlow(int &flow,int &ans)
{
int t;
flow=0;
ans=0;
while(t=SPFA(),t)
flow+=t,ans+=dis[T]*t;
}
}
char st[MAXN][MAXN],ed[MAXN][MAXN],ct[MAXN][MAXN];
const int dir[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
int main()
{
int n,m,cnt1=0,cnt2=0,S,T;
scanf("%d%d",&n,&m);
S=n*m*2+1,T=n*m*2+2;
for(int i=1;i<=n;i++)
{
scanf("%s",st[i]+1);
for(int j=1;j<=m;j++)
{
st[i][j]-='0';
cnt1+=st[i][j];
}
}
for(int i=1;i<=n;i++)
{
scanf("%s",ed[i]+1);
for(int j=1;j<=m;j++)
{
ed[i][j]-='0';
cnt2+=ed[i][j];
}
}
if(cnt1!=cnt2){printf("-1\n");return 0;}
for(int i=1;i<=n;i++)
{
scanf("%s",ct[i]+1);
for(int j=1;j<=m;j++)
{
ct[i][j]-='0';
}
}
ValueFlow::Init(S,T,T);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int flag=0;
if(st[i][j])
ValueFlow::add_edge(S,(i-1)*m+j,1,0),flag++;
if(ed[i][j])
ValueFlow::add_edge((i-1)*m+j+n*m,T,1,0),flag++;
ValueFlow::add_edge((i-1)*m+j,(i-1)*m+j+n*m,(ct[i][j]+flag)/2,1);
int x,y;
for(int d=0;d<8;d++)
{
x=i+dir[d][0],y=j+dir[d][1];
if(x<=0||y<=0||x>n||y>m)continue;
ValueFlow::add_edge((i-1)*m+j+n*m,(x-1)*m+y,1000,0);
}
}
int f,a;
ValueFlow::getValueFlow(f,a);
if(f==cnt1)printf("%d\n",a-cnt1);
else
{
printf("-1\n");
}
return 0;
}