1. 程式人生 > >【BJOI 2006】狼抓兔子(對偶圖)

【BJOI 2006】狼抓兔子(對偶圖)

題目連結

題解

明顯是求給定的圖的最小割。
但是如果直接跑最大流的話會爆炸。

我們發現這個圖有一個性質:它是一個平面圖(可平面圖)
我們考慮構造它的對偶圖,因為對偶圖的最短路即是原圖的最小割。
具體構圖方法見程式碼。

程式碼

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 2000005;
const int maxm = 6000005;
char ch;
bool
vis[maxn]; int n, m, x, ret, dis[maxn]; int tot, ter[maxm], nxt[maxm], len[maxm], lnk[maxn]; int read() { ch = getchar(); ret = 0; while (!isdigit(ch)) { ch = getchar(); } while (isdigit(ch)) { ret = (ret << 1) + (ret << 3) + (ch ^ '0'); ch = getchar(); } return
ret; } int id(int i, int j, int k) { if (i > n || j < 1) { return 0; } if (i < 1 || j > m) { return n * m << 1 | 1; } return (i - 1) * m + j + k * n * m; } void adde(int u, int v, int w) { ter[tot] = v; len[tot] = w; nxt[tot] = lnk[u]; lnk[u] = tot++; } int
spfa(int s, int t) { queue<int> que; que.push(s); memset(dis, 0x3f, sizeof(dis)); dis[s] = s, vis[s] = 1; for (int u, v, w; !que.empty(); ) { u = que.front(); que.pop(); vis[u] = 0; for (int i = lnk[u]; ~i; i = nxt[i]) { v = ter[i], w = len[i]; if (dis[v] > dis[u] + w) { dis[v] = dis[u] + w; if (!vis[v]) { vis[v] = 1; que.push(v); } } } } return dis[t]; } int main() { memset(lnk, -1, sizeof(lnk)); n = read(), m = read(), n--, m--; for (int i = 1; i <= n + 1; i++) { for (int j = 1; j <= m; j++) { x = read(); adde(id(i, j, 1), id(i - 1, j, 0), x); adde(id(i - 1, j, 0), id(i, j, 1), x); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m + 1; j++) { x = read(); adde(id(i, j, 0), id(i, j - 1, 1), x); adde(id(i, j - 1, 1), id(i, j, 0), x); } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { x = read(); adde(id(i, j, 0), id(i, j, 1), x); adde(id(i, j, 1), id(i, j, 0), x); } } printf("%d\n", spfa(0, n * m << 1 | 1)); return 0; }

總結

若需要求平面圖(通常是網格圖)的最小割(最大流),並且資料範圍極大,我們就需要使用本題中的技巧對題目進行求解。