題目描述
有一個n行m列的黑白棋盤,你每次可以交換兩個相鄰格子(相鄰是指有公共邊或公共頂點)中的棋子,最終達到目標狀態。要求第i行第j列的格子只能參與mi,j次交換。
輸入輸出格式
輸入格式:
第一行包含兩個整數n,m(1≤n,m≤20)。以下n行為初始狀態,每行為一個包含m個字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行為目標狀態,格式同初始狀態。以下n行每行為一個包含m個0..9數字的字符串,表示每個格子參與交換的次數上限。
輸出格式:
輸出僅一行,為最小交換總次數。如果無解,輸出?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;}
elseputs(
"-1");
return 0;
}
轉載于:https://www.cnblogs.com/ljt12138/p/6684333.html
總結
以上是生活随笔為你收集整理的[CQOI2012]交换棋子【网络流】【费用流】的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。