题解:P1791 [国家集训队] 人员雇佣

· · 题解

Problem

给定 N 个元素 a_i,你需要把它们分成两个集合 A, B。对于一个划分方案,代价为:

\left(\sum_{i \in A} a_i\right) + \sum_{i\in A}\left(\sum_{j\in A} E_{i, j} - \sum_{j\in B} E_{i, j}\right)

求划分的最小代价。

### Sol 这种二选一的东西,不难想到最小割。令 $s_i = \sum_{j} E_{i, j}$。 对于 $i \in [1, n]$,连边 $(S, i, s_i)$,$(i, T, A_i)$。 对于 $i \in [1, n], j \in [1, n]$,连边 $(i, j, 2E_{i, j})$。 求最小割即可。 ### Code 远古代码,看看就好。 ```cpp #include<bits/stdc++.h> #define ll long long #define sz(a) ((int) (a).size()) #define vi vector < int > #define pb emplace_back using namespace std; int n; int a[1010]; struct Flow { const ll inf = 1e18; int n, S, T; struct node { int v; ll w; int b; node(int _v, ll _w, int _b) { v = _v, w = _w, b = _b; } }; vector < vector < node > > e; vi dis, cur, q; Flow(int _n) : e(_n + 10), dis(_n + 10), cur(_n + 10), q(_n + 10) { n = _n; } void adde(int u, int v, ll w) { e[u].pb(v, w, sz(e[v])); e[v].pb(u, 0, sz(e[u]) - 1); } int hd, tl; bool bfs() { for(int i = 0; i <= n; ++i) dis[i] = cur[i] = 0; q[hd = tl = 1] = S; dis[S] = 1; while(hd <= tl) { int u = q[hd++]; for(auto i : e[u]) { int v = i.v; if(i.w && !dis[v]) { dis[v] = dis[u] + 1; q[++tl] = v; if(i.v == T) return 1; } } } return 0; } ll dfs(int u, ll flow) { if(u == T) return flow; ll res = 0; for(int &i = cur[u]; i < sz(e[u]); ++i) { int v = e[u][i].v; if(e[u][i].w && dis[v] == dis[u] + 1) { ll fl = dfs(v, min(flow, e[u][i].w)); e[u][i].w -= fl, e[v][e[u][i].b].w += fl; flow -= fl, res += fl; if(!flow) return res; } } return res; } ll maxflow(int _S, int _T) { S = _S, T = _T; ll res = 0; while(bfs()) res += dfs(S, inf); return res; } }; int main() { ios :: sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; int S = n + 1, T = n + 2; Flow G(T); for(int i = 1; i <= n; ++i) cin >> a[i], G.adde(i, T, a[i]); ll sum = 0; for(int i = 1; i <= n; ++i) { ll s = 0; for(int j = 1, x; j <= n; ++j) { cin >> x; s += x; G.adde(i, j, 2 * x); } G.adde(S, i, s); sum += s; } cout << sum - G.maxflow(S, T) << "\n"; return 0; } ```