【最小割/二分圖最大獨立集】【網絡流24題】【P2774】 方格取數問題
阿新 • • 發佈:2019-03-18
getc 如果 size etc lse fine std set solution
Description
給定一個 \(n~\times~m\) 的矩陣,每個位置有一個正整數,選擇一些互不相鄰的數,最大化權值和
Limitation
\(1~\leq~n,~m~\leq~100\)
Solution
由於數必須互不相鄰,考慮二分圖。
將矩陣染成二分圖,相鄰的格子連邊,這樣一條邊的兩個端點不能被同時選擇,問題就被轉化為了二分圖上的最大帶權獨立集問題。
有關二分圖的幾個定理:
二分圖最小無權點覆蓋 = 二分圖最大匹配
二分圖最小無權邊覆蓋 = 總點數 - 二分圖最大匹配
二分圖最大無權獨立集 = 總點數 - 二分圖最大匹配
如果點帶點 正 權,則源點向左部連邊,容量為點權,右部向匯點連邊,容量為點權,原邊保留,容量無窮。
二分圖最小權點覆蓋 = 最小割
二分圖最大權獨立集 = 點權和 - 最小割
最小點權覆蓋的證明與最大權閉合子圖的證明類似,證明在這裏,最大權獨立集的證明需要 最大獨立集 = 全集 - 最小點覆蓋 的引理。
於是這題跑一個最小割就可以解決了。
Code
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #ifdef ONLINE_JUDGE #define freopen(a, b, c) #endif typedef long long int ll; namespace IPT { const int L = 1000000; char buf[L], *front=buf, *end=buf; char GetChar() { if (front == end) { end = buf + fread(front = buf, 1, L, stdin); if (front == end) return -1; } return *(front++); } } template <typename T> inline void qr(T &x) { char ch = IPT::GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar(); while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar(); if (lst == '-') x = -x; } namespace OPT { char buf[120]; } template <typename T> inline void qw(T x, const char aft, const bool pt) { if (x < 0) {x = -x, putchar('-');} int top=0; do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10); while (top) putchar(OPT::buf[top--]); if (pt) putchar(aft); } const int maxn = 10010; const int maxm = 105; const int INF = 100000000; struct Edge { int u, v, flow; Edge *nxt, *bk; Edge(const int _u, const int _v, const int _flow, Edge* &h) { this->u = _u; this->v = _v; this->flow = _flow; this->nxt = h; h = this; } }; Edge *hd[maxn], *fir[maxn]; inline void cont(const int _u, const int _v, const int _flow) { auto u = new Edge(_u, _v, _flow, hd[_u]), v = new Edge(_v, _u, 0, hd[_v]); (u->bk = v)->bk = u; } int n, m, s, t, ans; int MU[maxn], id[maxm][maxm], col[maxm][maxm], dist[maxn]; std::queue<int>Q; bool bfs(); int dfs(const int u, int canag); void link(const int x, const int y); int main() { freopen("1.in", "r", stdin); qr(n); qr(m); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) { qr(MU[id[i][j] = ++t]); ans += MU[t]; } s = ++t; ++t; for (int i = 1; i <= n; ++i) { if ((col[i][1] = col[i - 1][1] ^ 1)) link(i, 1); else cont(id[i][1], t, MU[id[i][1]]); for (int j = 2; j <= m; ++j) if ((col[i][j] = col[i][j - 1] ^ 1)) link(i, j); else cont(id[i][j], t, MU[id[i][j]]); } while (bfs()) { for (int i = 1; i <= t; ++i) fir[i] = hd[i]; ans -= dfs(s, INF); } qw(ans, '\n', true); return 0; } bool bfs() { memset(dist, 0, sizeof dist); Q.push(s); dist[s] = 1; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (auto e = hd[u]; e; e = e->nxt) if (e->flow > 0) { int v = e->v; if (dist[v]) continue; dist[v] = dist[u] + 1; Q.push(v); } } return dist[t]; } int dfs(const int u, int canag) { if ((u == t) || (!canag)) return canag; int _f = 0; for (auto &e = fir[u]; e; e = e->nxt) if (e->flow > 0) { int v = e->v; if (dist[v] != (dist[u] + 1)) continue; int f = dfs(v, std::min(canag, e->flow)); e->flow -= f; e->bk->flow += f; _f += f; if (!(canag -= f)) break; } return _f; } void link(const int x, const int y) { int u = id[x][y]; cont(s, u, MU[u]); if (x > 1) cont(u, id[x - 1][y], INF); if (y < m) cont(u, id[x][y + 1], INF); if (y > 1) cont(u, id[x][y - 1], INF); if (x < n) cont(u, id[x + 1][y], INF); }
【最小割/二分圖最大獨立集】【網絡流24題】【P2774】 方格取數問題