1. 程式人生 > >BZOJ 2668 [cqoi2012]交換棋子 | 最小費用最大流

BZOJ 2668 [cqoi2012]交換棋子 | 最小費用最大流

logs -c 狀態 ace log urn problem template down

傳送門

BZOJ 2668

題解

同時分別限制流入和流出次數,所以把一個點拆成三個:入點in(x)、中間點mi(x)、出點ou(x)。

如果一個格子x在初始狀態是黑點,則連(S, mi(x), 1, 0)
如果x在目標狀態是黑點,則連(mi(x), T, 1, 0)
設x的交換次數限制是w
如果x在兩種狀態中顏色相同,則連(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0)
如果x只在初始狀態為黑色,則連(in(x), mi(x), w / 2, 0), (mi(x), ou(x), (w + 1) / 2, 0)
如果x只在目標狀態為黑色,則連(in(x), mi(x), (w + 1) / 2, 0), (mi(x), ou(x), w / 2, 0)

每個點x和相鄰的點y(八連通),連(ou(x), in(y), INF, 1)

然後最小費用最大流即可。

#include <queue>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using
namespace std; template <class T> void read(T &x){ char c; bool op = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') op = 1; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10
+ c - '0'; if(op) x = -x; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 1234, M = 1000005, INF = 0x3f3f3f3f; const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1}; const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1}; int n, m, tot, maxflow, mincost, src = 1, des = 2, id[23][23][3], b1, b2; int ecnt = 1, adj[N], pre[N], dis[N], nxt[M], go[M], cap[M], cost[M]; char st[23][23], ed[23][23], cp[23][23]; void _add(int u, int v, int w, int c){ go[++ecnt] = v; nxt[ecnt] = adj[u]; adj[u] = ecnt; cap[ecnt] = w; cost[ecnt] = c; } void add(int u, int v, int w, int c){ _add(u, v, w, c); _add(v, u, 0, -c); } bool spfa(){ queue <int> que; static bool inq[N] = {0}; for(int i = 1; i <= tot; i++) dis[i] = INF, pre[i] = 0; dis[src] = 0, que.push(src), inq[src] = 1; while(!que.empty()){ int u = que.front(); que.pop(), inq[u] = 0; for(int e = adj[u], v; e; e = nxt[e]){ if(cap[e] && dis[u] + cost[e] < dis[v = go[e]]){ dis[v] = dis[u] + cost[e], pre[v] = e; if(!inq[v]) que.push(v), inq[v] = 1; } } } return dis[des] < INF; } void mcmf(){ while(spfa()){ int delta = INF; for(int e = pre[des]; e; e = pre[go[e ^ 1]]) delta = min(delta, cap[e]); for(int e = pre[des]; e; e = pre[go[e ^ 1]]) cap[e] -= delta, cap[e ^ 1] += delta; maxflow += delta; mincost += delta * dis[des]; } } bool legal(int x, int y){ return x > 0 && y > 0 && x <= n && y <= m; } int main(){ read(n), read(m), tot = 3 * n * m + 2; for(int i = 1, cnt = 2; i <= n; i++) for(int j = 1; j <= m; j++) id[i][j][0] = ++cnt, id[i][j][1] = ++cnt, id[i][j][2] = ++cnt; for(int i = 1; i <= n; i++) scanf("%s", st[i] + 1); for(int i = 1; i <= n; i++) scanf("%s", ed[i] + 1); for(int i = 1; i <= n; i++) scanf("%s", cp[i] + 1); for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++){ int w = cp[i][j] - '0'; if(st[i][j] == '1') b1++, add(src, id[i][j][0], 1, 0); if(ed[i][j] == '1') b2++, add(id[i][j][0], des, 1, 0); if(st[i][j] == ed[i][j]){ add(id[i][j][1], id[i][j][0], w / 2, 0); add(id[i][j][0], id[i][j][2], w / 2, 0); } else if(st[i][j] == '1'){ add(id[i][j][1], id[i][j][0], w / 2, 0); add(id[i][j][0], id[i][j][2], (w + 1) / 2, 0); } else if(ed[i][j] == '1'){ add(id[i][j][1], id[i][j][0], (w + 1) / 2, 0); add(id[i][j][0], id[i][j][2], w / 2, 0); } for(int d = 0, x, y; d < 8; d++) if(legal(x = i + dx[d], y = j + dy[d])) add(id[i][j][2], id[x][y][1], INF, 1); } mcmf(); if(maxflow < max(b1, b2)) puts("-1"); else write(mincost), enter; return 0; }

BZOJ 2668 [cqoi2012]交換棋子 | 最小費用最大流