1. 程式人生 > 其它 >「Solution」 BZOJ3894 文理分科

「Solution」 BZOJ3894 文理分科

Problem

Link

題意:有 \(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\)

的路徑已經沒有了。只有通過 \(x,y\) 兩點的路了。

不難發現存在 \(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;
}