【题解】CQOI2012交换棋子
感受到网络流的强大了……这道题目的关键在于:
前后颜色不变的,流入流出的次数相等;原本是黑色的最后变成了白色,流出比流入次数多1;原本是白色最后变成黑色,流入比流出次数多一。所以我们将每一点拆成3个点,分别代表流入点,原点与流出点。最开始为黑色的点与源点连流量为1,费用为0的边,最后为黑色的点与汇点连流量为1,费用为0的边。
#include<bits/stdc++.h> using namespace std; #define maxn 300 #define maxm 8000 #define INF 99999 int n, m, size, tem, a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], head[maxm]; int cnp, fans, ans, cost, dis[maxm], pre[maxm], flow[maxm]; int dx[10] = {0, 0, 0, 1, 1, 1, -1, -1, -1}; int dy[10] = {0, 1, -1, 0, 1, -1, 1, 0, -1}; int s = 0, t; bool vis[maxm]; deque <int> q; struct edge { int to, last, f, c; }E[maxn * 400]; void add(int u, int v, int f, int c) { E[cnp].to = v, E[cnp].last = head[u], E[cnp].f = f, E[cnp].c = c; head[u] = cnp ++; E[cnp].to = u, E[cnp].last = head[v], E[cnp].f = 0, E[cnp].c = -c; head[v] = cnp ++; } int Get_id(int x, int y) { return (x - 1) * m + y; } void init() { memset(head, -1, sizeof(head)); } int SPFA() { q.push_back(s); flow[s] = INF; for(int i = 1; i <= n * m * 3 + 3; i ++) dis[i] = INF; while(!q.empty()) { int u = q.front(); q.pop_front(); vis[u] = false; for(int i = head[u]; i != -1; i = E[i].last) { int v = E[i].to; if(E[i].f && dis[v] > dis[u] + E[i].c) { dis[v] = dis[u] + E[i].c, pre[v] = i; flow[v] = min(flow[u], E[i].f); if(!vis[v]) { vis[v] = true; if(!q.empty() && dis[v] < dis[q.front()]) q.push_front(v); else q.push_front(v); } } } } if(dis[t] >= INF) return false; else return true; } void Max_flow() { while(SPFA()) { int v = pre[t]; while(2333) { E[v].f -= flow[t]; E[v ^ 1].f += flow[t]; if(E[v ^ 1].to == s) break; v = pre[E[v ^ 1].to]; } ans += flow[t]; cost += flow[t] * dis[t]; } } void Get_input() { for(int i = 1; i <= n; i ++) { string s; cin >> s; for(int j = 0; j < m; j ++) a[i][j + 1] = s[j] - '0'; } for(int i = 1; i <= n; i ++) { string s; cin >> s; for(int j = 0; j < m; j ++) b[i][j + 1] = s[j] - '0'; } for(int i = 1; i <= n; i ++) { string s; cin >> s; for(int j = 0; j < m; j ++) c[i][j + 1] = s[j] - '0'; } } void Connect() { for(int i = 1; i <= n; i ++) for(int j = 1; j <= m; j ++) { int u = Get_id(i, j); if(a[i][j]) tem ++, add(s, u, 1, 0); if(b[i][j]) fans ++, add(u, t, 1, 0); if(a[i][j] == b[i][j]) { add(u + size, u, c[i][j] / 2, 1); add(u, u + 2 * size, c[i][j] / 2, 1); } else if(b[i][j]) { add(u + size, u, (c[i][j] + 1) / 2, 1); add(u, u + 2 * size, c[i][j] / 2, 1); } else if(a[i][j]) { add(u + size, u, c[i][j] / 2, 1); add(u, u + 2 * size, (c[i][j] + 1) / 2, 1); } for(int k = 1; k <= 8; k ++) { int x = i + dx[k], y = j + dy[k]; if(x < 1 || x > n || y < 1 || y > m) continue; add(u + 2 * size, Get_id(x, y) + size, INF, 0); } } } int main() { scanf("%d%d", &n, &m); init(); t = n * m * 3 + 3, size = n * m; Get_input(); Connect(); if(tem != fans) { printf("-1\n"); return 0; } Max_flow(); if(ans == fans) printf("%d", cost >> 1); else printf("-1\n"); return 0; }