Crimiville
Problem
You are given a bipartite graph G=(X∪Y, E)\(G=(X\cup Y, E)\). Is there a S ⊆ X \(S\subseteq X\) such that |Γ(S)| < |S|\(\lvert\Gamma(S)\rvert\lt\lvert S\rvert\)? If not, maximize ∑i∈Sw(i) − ∑i∈Γ(S)w(i) \(\sum_{i\in S}w(i)-\sum_{i\in\Gamma(S)}w(i)\) subject to |Γ(S)| = |S|\(\lvert\Gamma(S)\rvert=\lvert S\rvert\).
Solution
As a direct consequences of Hall's theorem, we just have to check if there is a perfect matching to answer the first question.
The "subject to" part of the second question is annoying. We can add a sufficiently large number to each weight, so any infeasible solution won't be the maximum.
Finally, with max(∑i∈Sw(i) − ∑i∈Γ(S)w(i)) = ∑i∈Xw(i) − min(∑i∈Γ(S)w(i) + ∑i∈X\Sw(i)) $$\max\left(\sum_{i\in S}w(i)-\sum_{i\in\Gamma(S)}w(i)\right)=\sum_{i\in X}w(i)-\min\left(\sum_{i\in\Gamma(S)}w(i)+\sum_{i\in X\setminus S}w(i)\right)$$ we can get the answer after computing the min-cut of a graph similar to the original one.
Source Code
#include <cstdio>
#include <list>
#include <numeric>
using namespace std;
constexpr int maxn = 200 * 2 + 2;
int c[maxn][maxn], f[maxn][maxn], e[maxn], h[maxn];
void push(int u, int v) {
int x = min(c[u][v] - f[u][v], e[u]);
if(x) {
f[u][v] += x;
f[v][u] -= x;
e[u] -= x;
e[v] += x;
}
}
void relabel(int u, int n) {
h[u] = 2 * n;
for(int i = 0; i < n; i++)
if(c[u][i] > f[u][i])
h[u] = min(h[u], h[i] + 1);
}
void discharge(int u, int n) {
while(e[u]) {
for(int i = 0; i < n && e[u]; i++)
if(h[u] == h[i] + 1)
push(u, i);
if(e[u])
relabel(u, n);
}
}
int max_flow(int s, int t, int n) {
for(int i = 0; i < n; i++)
fill_n(f[i], n, 0);
for(int i = 0; i < n; i++) {
f[s][i] = e[i] = c[s][i];
f[i][s] = -f[s][i];
}
fill_n(h, n, 0);
h[s] = n;
list<int> q;
for(int i = 0; i < n; i++)
if(i != s && i != t)
q.push_back(i);
for(auto i = q.begin(); i != q.end(); ++i) {
int oh = h[*i];
discharge(*i, n);
int nh = h[*i];
if(oh != nh)
q.splice(q.begin(), q, i);
}
return accumulate(f[s], f[s] + n, 0);
}
constexpr int maxda = 10000;
constexpr int large = 200 * maxda + 1;
constexpr int maxc = maxda + large;
void solve() {
int n;
scanf("%d", &n);
int v = 2 * n + 2;
for(int i = 0; i < v; i++)
fill_n(c[i], v, 0);
for(int i = 0; i < n; i++) {
int k;
scanf("%d", &k);
for(int j = 0; j < k; j++) {
int x;
scanf("%d", &x);
c[x][n + i] = 1;
}
}
for(int i = 0; i < n; i++) {
c[v - 2][i] = 1;
c[n + i][v - 1] = 1;
}
if(max_flow(v - 2, v - 1, v) != n) {
for(int i = 0; i < 2 * n; i++)
scanf("%*d");
puts("need reinforcement.");
return;
}
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(c[i][n + j])
c[i][n + j] = maxc;
for(int i = 0; i < n; i++) {
int a;
scanf("%d", &a);
c[n + i][v - 1] = a + large;
}
int sumd = 0;
for(int i = 0; i < n; i++) {
int d;
scanf("%d", &d);
sumd += c[v - 2][i] = d + large;
}
printf("%d\n", sumd - max_flow(v - 2, v - 1, v));
}
int main() {
int t;
scanf("%d", &t);
while(t--)
solve();
}