有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。
先将棋盘看成只有黑子,白子看成是空白的,只需考虑黑子
然后相同的点交换是没有意义的,所以只需要考虑黑子进入和出去的次数。
如果一个点初始和最终的状态相同,说明黑子进入和出去的次数相同,上限 w 2 \frac{w}{2} 2w
如果初始为黑子最终为白子,说明出去的次数多一次,进入上限 w − 1 2 \frac{w-1}{2} 2w−1,出去上 w + 1 2 \frac{w+1}{2} 2w+1
如果初始为白子最终为黑子,说明进入的次数多一次,和上面类似
对次数的限制,可以将每个点拆成入点,原点,出点,容量为上限。
每个点的出点向八连通入点连边,S向初始黑连边,终黑向T连边
如果最大流不是黑子个数,就是-1,不然就是最小费用
#include
#include
#include
using namespace std;
#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);putchar('\n');}void wsp(int x) {write(x);putchar(' ');}
const int N = 25, M = 3*N*N, inf = 1010580540;
const int mx[8]={1,1,1,0,0,-1,-1,-1},my[8]={1,0,-1,1,-1,1,0,-1};
struct edge {
int t,nxt,w,v;
}e[M*10];
struct node {
int x,v;
bool operator <(node a) const{
return v > a.v;
}
};
int n,m,cnt,Ct,S,T,tim,num,flow,cost;
char s1[N][N],s2[N][N],s3[N][N];
int head[M],dis[M],vis[M],h[M],pre[M];
priority_queue<node> q;
int id(int x, int y, int p) {return (x-1)*m+y+p*Ct;}
void add(int u, int t, int w, int v) {
// printf("%d %d %d %d\n",u,t,w,v);
e[++cnt] = (edge){t,head[u],w,v}; head[u] = cnt;
e[++cnt] = (edge){u,head[t],0,-v}; head[t] = cnt;
}
bool dij() {
++tim;
memset(dis, 60, sizeof dis);
dis[S] = 0; q.push((node){S, 0});
while (!q.empty()) {
int u = q.top().x; q.pop();
if (vis[u] == tim) continue;
vis[u] = tim;
for (int i = head[u]; i; i = e[i].nxt) {
int t = e[i].t, v = e[i].v;
if (e[i].w > 0 && dis[t] > dis[u]+v+h[u]-h[t]) {
dis[t] = dis[u]+v+h[u]-h[t];
pre[t] = i;
q.push((node){t, dis[t]});
}
}
}
// for (int i = 1; i <= T; i++) wsp(pre[i]);
// puts("");
return dis[T] != inf;
}
int aug(int x, int Flow) {
if (x == S) return Flow;
int tmp = aug(e[pre[x]^1].t, min(Flow, e[pre[x]].w));
e[pre[x]].w-=tmp; e[pre[x]^1].w+=tmp;
return tmp;
}
void min_flow_cost() {
while (dij()) {
for (int i = 1; i <= T; i++) h[i]+=dis[i];
int tmp = aug(T, inf);
flow+=tmp;
cost += tmp*h[T];
}
}
int main() {
// freopen("2668.in", "r", stdin);
// freopen("2668.out", "w", stdout);
n = read(); m = read();
Ct = n*m; cnt=1;
S = Ct*3+1; T = Ct*3+2;
for (int i = 1; i <= n; ++i) scanf("%s",s1[i]+1);
for (int i = 1; i <= n; ++i) scanf("%s",s2[i]+1);
for (int i = 1; i <= n; ++i) scanf("%s",s3[i]+1);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; j++) {
int w = s3[i][j]-'0';
if (s1[i][j] == s2[i][j]) add(id(i,j,0), id(i,j,1), w/2, 0), add(id(i,j,1), id(i,j,2), w/2, 0);
else if (s1[i][j] == '1') {
add(S, id(i,j,1), 1, 0), num++;
add(id(i,j,0), id(i,j,1), (w-1)/2, 0), add(id(i,j,1), id(i,j,2), (w+1)/2, 0);
}
else {
add(id(i,j,1), T, 1, 0);
add(id(i,j,0), id(i,j,1), (w+1)/2, 0), add(id(i,j,1), id(i,j,2), (w-1)/2, 0);
}
for (int k = 0; k < 8; k++) {
int x = i+mx[k], y = j+my[k];
if (x < 1 || x > n || y < 1 || y > m) continue;
add(id(i,j,2), id(x,y,0), inf, 1);
}
}
min_flow_cost();
printf("%d\n",flow != num ? -1 : cost);
}