P1791 [国家集训队]人员雇佣 题解
LittleMoMol · · 题解
前言
大家都能看出来是一个最小割,在此主要阐述一下连边的正确性。
博客欢迎你,为你开天辟地
连边方式
- 源点
S 向每个点(经理)i ,连容量为\sum\limits_{k=1}^n E_{i,k} 的边。 - 每个点(经理)
i 向汇点T ,连容量为A_i 的边。 - 每个点(经理)
i 向其他点(经理)j ,连容量为2 E_{i, j} 的边。 - 答案为
\sum\limits_{i=1}^n \sum\limits_{j=1}^n E(i,j) - \operatorname{dinic} 。
理论基础
答案最大是多少?自然是所有
我们令连向
这个图说明每个经理即雇佣了又没有雇佣,呈一种薛定谔的状态。
也就是说每个点我们只可以连
不考虑冲突,假如我们割去某个点与
不考虑冲突,假如我们割去
考虑冲突,为什么要那样连边?先上图!
这个图的情况是选
首先如果两个点都连接于 你们都在一个团队里了还互相残杀就说不过去了。
当 惊不惊喜?意不意外?,所以团队总贡献一下子少了
这告诉我们一个真理:不要把自己完全裸露在对方的视野里,要留一手备长局,步入社会后就是这样。
所以说跑一边最大流求最小割,用总价值减去它即可。
Code
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010 * 1010, M = 2 * (N * 3);
const int INF = 0x3f3f3f3f;
int n, S, T, sum;
int maze[1010][1010];
int h[N], e[M], f[M], ne[M], idx;
int q[N], cur[N], depth[N];
int read()
{
int s = 0, w = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9')
{
s = s * 10 + c - '0';
c = getchar();
}
return s * w;
}
void add(int a, int b, int c)
{
e[idx] = b;
f[idx] = c;
ne[idx] = h[a];
h[a] = idx ++ ;
e[idx] = a;
f[idx] = 0;
ne[idx] = h[b];
h[b] = idx ++ ;
return;
}
bool bfs()
{
int hh = 0, tt = 0;
memset(depth, -1, sizeof depth);
q[0] = S;
depth[S] = 0;
cur[S] = h[S];
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int ver = e[i];
if (depth[ver] == -1 && f[i] > 0)
{
depth[ver] = depth[t] + 1;
cur[ver] = h[ver];
q[ ++ tt] = ver;
if (ver == T) return true;
}
}
}
return false;
}
int dfs(int u, int lmt)
{
if (u == T) return lmt;
int flow = 0;
for (int i = cur[u]; ~i && flow < lmt; i = ne[i])
{
cur[u] = i;
int ver = e[i];
if (depth[ver] == depth[u] + 1 && f[i] > 0)
{
int t = dfs(ver, min(f[i], lmt - flow));
if (!t) depth[ver] = -1;
f[i] -= t;
f[i ^ 1] += t;
flow += t;
}
}
return flow;
}
int dinic()
{
int res = 0, flow = 0;
while (bfs())
while (flow = dfs(S, INF))
res += flow;
return res;
}
int main()
{
n = read();
S = 0, T = n + 1;
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
{
int a = read();
add(i, T, a);
}
for (int i = 1; i <= n; i ++ )
{
int tot = 0;
for (int j = 1; j <= n; j ++ )
{
cin >> maze[i][j];
sum += maze[i][j];
tot += maze[i][j];
}
add(S, i, tot);
}
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
{
if (i == j) continue;
add(i, j, 2 * maze[i][j]);
}
cout << sum - dinic() << endl;
return 0;
}
后语
思考是好的,完结撒花!