Description
Solution
Part 1: 简化版
首先,考虑这样一个简化版的问题: 第 i i i 行第 j j j 列的点被经过不超过 l i m i , j lim_{i,j} limi,j 次时,求最少移动步数。
首先,我们把白棋看做没棋。特别的,若 a i , j = b i , j = 1 a_{i,j}=b_{i,j}=1 ai,j=bi,j=1(即对应位均为黑棋),那么我们也将其均看做没棋。
考虑网络流建模。为满足点的限制,将每一个 ( i , j ) (i,j) (i,j) 拆成入点 L i , j L_{i,j} Li,j 和出点 R i , j R_{i,j} Ri,j。
- ∀ ( i , j ) \forall (i,j) ∀(i,j),若 a i , j = 1 a_{i,j}=1 ai,j=1,连边 ( s , L i , j , 1 , 0 ) (s,L_{i,j},1,0) (s,Li,j,1,0)
- ∀ ( i , j ) \forall (i,j) ∀(i,j),若 b i , j = 1 b_{i,j}=1 bi,j=1,连边 ( R i , j , t , 1 , 0 ) (R_{i,j},t,1,0) (Ri,j,t,1,0)
- ∀ ( i , j ) \forall (i,j) ∀(i,j),连边 ( L i , j , R i , j , l i m i , j , 0 ) (L_{i,j},R_{i,j},lim_{i,j},0) (Li,j,Ri,j,limi,j,0)
- ∀ ( x 0 , y 0 ) \forall (x_0,y_0) ∀(x0,y0),对于所有与其相邻的 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),连边 ( R x 0 , y 0 , L x 1 , y 1 , ∞ , 1 ) (R_{x_0,y_0},L_{x_1,y_1},∞,1) (Rx0,y0,Lx1,y1,∞,1)
跑最小费用最大流即可。
Part 2: 原题
回到原题。
此时 l i m i , j lim_{i,j} limi,j 作为了交换次数的上界,这意味着什么呢?不难发现,对于某条形如 a → b → c → d a \to b \to c \to d a→b→c→d 的棋子移动路径, b , c b,c b,c 会额外参与两次交换而 a , d a,d a,d 只会参与一次交换。
因此,我们需要把 L i , j L_{i,j} Li,j 和 R i , j R_{i,j} Ri,j 之间的边的流量修改为 ⌊ l i m i , j 2 ⌋ \lfloor \frac {lim_{i,j}} {2} \rfloor ⌊2limi,j⌋。特别的,若 a i , j a_{i,j} ai,j 与 b i , j b_{i,j} bi,j 有二者之一为 0 0 0(注意在 Part 1 中我们已经把 a i , j = b i , j = 1 a_{i,j}=b_{i,j}=1 ai,j=bi,j=1 的情况给处理掉了),那么我们再附上一条流量为 1 1 1 的边。
时间复杂度 O ( M C M F ( n 2 , n 2 ) ) O(MCMF(n^2,n^2)) O(MCMF(n2,n2)),可以通过本题。
Code
#include <bits/stdc++.h>
#define int long long
#define inf 20000000000007
using namespace std;
const int maxn=1005,maxp=3005;
int read(){
int s=0,w=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') w=-w;ch=getchar();}
while (ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^'0');ch=getchar();}
return s*w;
}
int n,m,s,t,sum,cnt=1;
int head[maxp],a[maxn][maxn],b[maxn][maxn],lim[maxn][maxn];
struct edge{int nxt,to,f,d;}e[2000*maxp];
int dx[8]={-1,-1,-1, 0, 0, 1, 1, 1};
int dy[8]={-1, 0, 1,-1, 1,-1, 0, 1};
void add_edge(int u,int v,int f,int d){
cnt++;
e[cnt].to=v,e[cnt].f=f,e[cnt].d=d;
e[cnt].nxt=head[u],head[u]=cnt;
}
void Add(int u,int v,int f,int d){
// cout<<"ADD:"<<u<<' '<<v<<' '<<f<<' '<<d<<endl;
add_edge(u,v,f,d),add_edge(v,u,0,-d);
}
namespace MCMF{
int dis[maxp],flow[maxp],pre[maxp],last[maxp],vis[maxp];
bool SPFA(){
for (int i=s;i<=t;i++) dis[i]=flow[i]=inf,vis[i]=0;
queue<int> q;
q.push(s),dis[s]=0,vis[s]=1;
while (!q.empty()){
int now=q.front();
q.pop();vis[now]=0;
for (int i=head[now];i;i=e[i].nxt){
int y=e[i].to;
if (dis[y]>dis[now]+e[i].d&&e[i].f>0){
dis[y]=dis[now]+e[i].d;
flow[y]=min(flow[now],e[i].f);
pre[y]=now,last[y]=i;
if (!vis[y]) q.push(y),vis[y]=1;
}
}
}
if (dis[t]>=inf) return false;
else return true;
}
int calc(){
int mincost=0,maxflow=0;
while (SPFA()){
int now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while (now!=s){
e[last[now]].f-=flow[t];
e[last[now]^1].f+=flow[t];
now=pre[now];
}
}
return mincost;
}
}
namespace ducati{
int pos(int x,int y,int d){return d*n*m+(x-1)*m+y;}
void get_all_in(){
n=read(),m=read();
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
char x;cin>>x;
a[i][j]=x-'0',sum-=a[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
char x;cin>>x;
b[i][j]=x-'0',sum+=b[i][j];
}
}
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
char x;cin>>x;
lim[i][j]=x-'0';
}
}
}
void build(){
s=0,t=2*n*m+1;
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
if (a[i][j]&b[i][j]) a[i][j]=b[i][j]=0;
int x=pos(i,j,0),y=pos(i,j,1);
if (a[i][j]) Add(s,x,1,0);
if (b[i][j]) Add(y,t,1,0);
Add(x,y,lim[i][j]/2,0);
if (a[i][j]|b[i][j]){
if (lim[i][j]&1) Add(x,y,1,0);
}
for (int k=0;k<8;k++){
int ii=i+dx[k],jj=j+dy[k];
if (ii>=1&&ii<=n&&jj>=1&&jj<=m)
Add(y,pos(ii,jj,0),inf,1);
}
}
}
}
void solve(){
get_all_in(),build();
if (sum==0) cout<<MCMF::calc()<<endl;
else cout<<-1<<endl;
}
}
signed main(){
ducati::solve();
return 0;
}