题目描述
有一个
n
行
输入输出格式
输入格式:
第一行包含两个整数
n,m(1≤n,m≤20)
。以下
n
行为初始状态,每行为一个包含
输出格式:
输出仅一行,为最小交换总次数。如果无解,输出 −1 。
解法:
一眼费用流系列…然而这题的处理有点诡异,首先要拆点将点的限制转化为边的限制, S→INi,OUTi→T 。 IN→OUT 的权值是多少?分三种情况讨论:
- 既不是起始也不是结束状态: ⌊n2⌋
- 既是起始也是结束状态: ⌊n+22⌋
- 是起始或结束状态: ⌊n+12⌋
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
struct node {
int to, next, f, c, neg;
} edge[MAXN*40];
int head[MAXN], top = 0;
void push(int i, int j, int k, int l)
{
if (i*j == 0) return;
++top, edge[top] = (node) {j, head[i], k, l, top+1}, head[i] = top;
++top, edge[top] = (node) {i, head[j], 0, -l, top-1}, head[j] = top;
}
int vis[MAXN], dis[MAXN], S = 2001, T = 2002;
queue<int> que;
int pre[MAXN], pre_edge[MAXN];
const int INF = 233333333;
bool spfa()
{
memset(dis, 127/3, sizeof dis);
memset(pre, 0, sizeof pre);
vis[S] = 1, que.push(S), dis[S] = 0;
while (!que.empty()) {
int tp = que.front(); que.pop(), vis[tp] = 0;
for (int i = head[tp]; i; i = edge[i].next) {
if (edge[i].f == 0 || dis[edge[i].to] <= dis[tp] + edge[i].c) continue;
int to = edge[i].to, c = edge[i].c;
dis[to] = dis[tp] + c, pre[to] = tp, pre_edge[to] = i;
if (!vis[to])
vis[to] = 1, que.push(to);
}
}
return dis[T] < INF;
}
int sap(int &cost)
{
int ans = INF;
for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f);
for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans;
cost += ans*dis[T];
return ans;
}
int mst(int &cost)
{
cost = 0;
int ans = 0;
while (spfa()) ans += sap(cost);
return ans;
}
int n, m;
int A[30][30], B[30][30], C[30][30];
char str[30];
int readln(int A[30][30])
{
int cnt = 0;
for (int i = 1; i <= n; i++) {
scanf("%s", str+1);
for (int j = 1; j <= m; j++)
A[i][j] = str[j]-'0', cnt += A[i][j];
}
return cnt;
}
inline int number(int i, int j, int id)
{ return (i<=0||i>n||j<=0||j>m)?0:id*n*m+(i-1)*m+j; }
int dx[] = {1,0,-1,0, 1, 1, -1, -1}, dy[] = {0,1,0,-1, -1, 1, -1, 1};
int main()
{
scanf("%d%d", &n, &m);
int a = readln(A);
int b = readln(B); readln(C);
if (a != b) {puts("-1"); return 0;}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (A[i][j] == 1) push(S, number(i, j, 0), 1, 0);
if (B[i][j] == 1) push(number(i, j, 1), T, 1, 0);
if (A[i][j] == 1 && B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+2)/2, 1);
else if (A[i][j] == 1 || B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+1)/2, 1);
else push(number(i, j, 0), number(i, j, 1), C[i][j]/2, 1);
for (int k = 0; k < 8; k++) push(number(i, j, 1), number(i+dx[k], j+dy[k], 0), INF, 0);
}
int ans = 0, cost = 0;
ans = mst(cost);
if (ans == a) {
cout << cost-a << endl;
} else
puts("-1");
return 0;
}