「Solution」 BZOJ3894 文理分科
Problem
題意:有 \(n*m\) 個同學,每個同學選科目有一個滿意值,若 ta 上下左右的同學與 ta 選的科目相同則有額外的滿意值,求最大的滿意值。
Solution
利用最小割的思路來做,問題轉化為 總貢獻-最小割。
考慮建模。
\(s \rightarrow i\) 容量為 \(i\) 選文科的貢獻。
\(i \rightarrow t\) 容量為 \(i\) 選理科的貢獻。
\(a1,a2,a3,a4\) 同理。
\(s \rightarrow x\) 容量為同選文科的貢獻。
\(y \rightarrow t\) 容量為同選理科的貢獻。
我們來證明一下,每一個割法下來都是一個合法(符合題目定義)的方案。
一條容量為正無窮的邊,則意味這這條邊不會被割。
- 當 \(i,a1,a2,a3,a4\) 選理。
則 \(s \rightarrow i\) 被割掉,\(a1,a2,a3,a4\) 同樣被割。
最後發現還可以通過 \(s \rightarrow x\) 走向 \(T\) ,則 \(s \rightarrow x\) 也要被割掉。所以割貢獻就是 \(i,a1,a2,a3,a4\) 的選文的貢獻 + 同文的貢獻。最終得到的就是都選理的貢獻。(都選文科同理)
- 當 \(i\) 選理,\(a1\) 選文,\(a2-a4\) 隨便選。即選科不相同。
這說明已經不能得到同選理科的貢獻了。
割掉每個點沒選的科目的邊後,則直接通過 \(S \rightarrow node \rightarrow T\)
不難發現存在 \(S \rightarrow a1 \rightarrow y \rightarrow T\) 的路徑,由於 \(S \rightarrow a1\) 不能割,\(a1 \rightarrow x\) 容量為無窮也不能割,所以只能割掉 \(y \rightarrow T\)。\(S \rightarrow x \rightarrow i \rightarrow T\) 同理。
所以既不是全選文科,也不是全選理科的情況一定不會算上同選文和同選理的貢獻。
Code
//by Poison //c++11 Dinic #include <queue> #include <cstdio> #include <vector> #include <algorithm> using namespace std; #define Maxn 500 #define LL long long #define rep(i, j, k) for(int i = (j); i <= (k); i ++) #define per(i, j, k) for(int i = (j); i >= (j); i --) int dx[4] = {0, 1, 0, -1}; int dy[4] = {1, 0, -1, 0}; struct Score { int art, science; int sart, sscience; } a[Maxn + 5][Maxn + 5]; struct Flow_Edge { int v, u; long long cap, flow = 0; Flow_Edge (int v, int u, LL cap) : v (v), u (u), cap (cap) {} } ; struct Dinic { const LL Maxn_Flow = 1e18; int n, m = 0, s, t; queue < int > q; vector < int > ptr, level; vector < Flow_Edge > edge; vector < vector < int > > adj; Dinic (int n, int s, int t) : n (n), s (s), t (t) { adj.resize (n + 1); ptr.resize (n + 1); level.resize (n + 1); } void Add_Edge (int v, int u, LL c) { edge.emplace_back (v, u, c); edge.emplace_back (u, v, 0); adj[v].push_back (m); adj[u].push_back (m + 1); m += 2; } bool Bfs () { while (q.size ()) { int v = q.front (); q.pop (); for (int id : adj[v]) { if (edge[id].cap - edge[id].flow < 1) continue; if (level[edge[id].u] != -1) continue; level[edge[id].u] = level[v] + 1; q.push (edge[id].u); } } return level[t] != -1; } LL Dfs (int v, LL pushed) { if (!pushed) return 0; if (v == t) return pushed; for (int& i = ptr[v]; i < (int) adj[v].size (); i ++) { int id = adj[v][i]; int u = edge[id].u; if (level[v] + 1 != level[u] || edge[id].cap - edge[id].flow < 1) continue; LL tr = Dfs (u, min (pushed, edge[id].cap - edge[id].flow)); if (!tr) continue; edge[id].flow += tr; edge[id ^ 1].flow -= tr; return tr; } return 0; } LL Flow () { LL f = 0; while (true) { fill (level.begin (), level.end (), -1); level[s] = 0; q.push (s); if (!Bfs ()) break; fill (ptr.begin (), ptr.end (), 0); while (LL pushed = Dfs (s, Maxn_Flow)) f += pushed; } return f; } } ; int main () { int n, m; scanf ("%d %d", &n, &m); int tot = 0; rep (i, 1, n) rep (j, 1, m) { scanf ("%d", &a[i][j].art); tot += a[i][j].art; } rep (i, 1, n) rep (j, 1, m) { scanf ("%d", &a[i][j].science); tot += a[i][j].science; } rep (i, 1, n) rep (j, 1, m) { scanf ("%d", &a[i][j].sart); tot += a[i][j].sart; } rep (i, 1, n) rep (j, 1, m) { scanf ("%d", &a[i][j].sscience); tot += a[i][j].sscience; } Dinic T (n * m * 3 + 1, 0, n * m * 3 + 1); int s = 0, t = n * m * 3 + 1; rep (i, 1, n) { rep (j, 1, m) { T.Add_Edge (s, (i - 1) * m + j, a[i][j].art); T.Add_Edge ((i - 1) * m + j, t, a[i][j].science); T.Add_Edge (s, n * m + (i - 1) * m + j, a[i][j].sart); T.Add_Edge (n * m * 2 + (i - 1) * m + j, t, a[i][j].sscience); T.Add_Edge (n * m + (i - 1) * m + j, (i - 1) * m + j, 1e18); T.Add_Edge ((i - 1) * m + j, n * m * 2 + (i - 1) * m + j, 1e18); rep (k, 0, 3) { int x = i + dx[k]; int y = j + dy[k]; if (x < 1 || y < 1 || x > n || y > m) continue; T.Add_Edge (n * m + (i - 1) * m + j, (x - 1) * m + y, 1e18); T.Add_Edge ((x - 1) * m + y, n * m * 2 + (i - 1) * m + j, 1e18); } } } printf ("%lld", tot - T.Flow ()); return 0; }