【二分圖最大點獨立集/最小割】P2774 方格取數問題
阿新 • • 發佈:2020-12-17
P2774 方格取數問題
思路
和騎士共存問題類似。同樣將頂點分為奇數點和偶數點,則可以全部只取奇數點,或全部只取偶數點;若取了一個奇數點\((i,j)\),則受影響的只有一部分偶數點,即無法取得的與其有公共邊的頂點\((i-1,j),(i+1,j),(i,j-1),(i,j+1)\),而所有奇數點都不受影響;偶數點同理。
由此可以抽象成二分圖模型,奇數點為左部點,偶數點為右部點,在互相影響的頂點間連一條有向邊。注意必須從右部點出發,連向左部點。如果反過來連會WA(為什麼?)。然後問題即求二分圖最大獨立集=總點權-最大匹配(最小割).
寫錯一堆細節……
- 座標雜湊,應該是\((x-1)*m+y\)
- 判斷點是奇數點還是偶數點,是用
(i+j)&1
,不是solve(i,j)&1
!! - 頂點內部連邊,只能在連向了匯點的頂點裡遍歷,不能在被源點連向的頂點裡遍歷,前者AC,後者全部WA(為什麼?)
程式碼
const int INF = 0x3f3f3f3f; const int maxn = 6e2 + 100; const int maxm = 2e4 + 100; int cnt_e = 1; int head[maxn]; int n, m; int s, t; int cur[maxn], depth[maxn], gap[maxn]; LL Maxflow = 0; struct Edge { int from, to, next; LL w; }e[maxm]; void addn(int& cnt_e, int head[], Edge e[], int u, int v, LL w) { //網路流建圖 e[++cnt_e].next = head[u]; e[cnt_e].from = u; e[cnt_e].to = v; e[cnt_e].w = w; head[u] = cnt_e; e[++cnt_e].next = head[v]; e[cnt_e].from = v; e[cnt_e].to = u; e[cnt_e].w = 0; head[v] = cnt_e; } void bfs() { mem(depth, -1); mem(gap, 0); depth[t] = 0; gap[0] = 1; cur[t] = head[t]; queue<int> q; q.push(t); while (q.size()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = e[i].next) { int v = e[i].to; if (depth[v] != -1) continue; q.push(v); depth[v] = depth[u] + 1; gap[depth[v]]++; } } return; } LL dfs(int now, LL minflow,int n) { if (now == t) { Maxflow += minflow; return minflow; } LL nowflow = 0; for (int i = cur[now]; i; i = e[i].next) { cur[now] = i; int v = e[i].to; if (e[i].w && depth[v] + 1 == depth[now]) { LL k = dfs(v, min(e[i].w, minflow - nowflow), n); if (k) { e[i].w -= k; e[i ^ 1].w += k; nowflow += k; } if (minflow == nowflow) return nowflow; } } gap[depth[now]]--; if (!gap[depth[now]]) depth[s] = n + 1; depth[now]++; gap[depth[now]]++; return nowflow; } LL ISAP(int n) { Maxflow = 0; bfs(); while (depth[s] < n) { memcpy(cur, head, sizeof(head)); dfs(s, INF, n); } return Maxflow; } int solve(int x, int y) { return (x - 1) * m + y; } int dirx[] = { 0,0,1,-1 }; int diry[] = { 1,-1,0,0 }; int main() { ios::sync_with_stdio(false); cin >> n >> m; s = n * m + 1; t = s + 1; LL sum = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { LL val; cin >> val; sum += val; if ((i + j) & 1) { addn(cnt_e, head, e, s, solve(i, j), val); } else { addn(cnt_e, head, e, solve(i, j), t, val); for (int k = 0; k < 4; k++) { int nx = i + dirx[k]; int ny = j + diry[k]; if (nx<1 || ny<1 || nx>n || ny>m) continue; addn(cnt_e, head, e, solve(i, j), solve(nx, ny), INF); } } } } ISAP(n * m + 2); cout << sum - Maxflow; return 0; }