#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m, s, t;
vector<pair<pair<int, int>, int>> vec[10005];
int vis[10005];
struct edge
{
int v1, w1, id;
} pre[10005];
bool bfs()
{
queue<int> que;
memset(vis, 0, sizeof(vis));
vis[s] = 1;
que.push(s);
while (!que.empty())
{
int v = que.front();
que.pop();
if (v == t)
return 1;
for (int i = 1; i < vec[v].size(); i++)
{
if (vec[v][i].first.second && !vis[vec[v][i].first.first])
{
vis[vec[v][i].first.first] = 1;
pre[vec[v][i].first.first].v1 = v;
pre[vec[v][i].first.first].w1 = vec[v][i].first.second;
pre[vec[v][i].first.first].id = i;
que.push(vec[v][i].first.first);
}
}
}
return 0;
}
signed main()
{
cin >> n >> m >> s >> t;
for (int i = 1; i <= n; i++)
{
vec[i].push_back({{0, 0}, 0});
}
for (int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
vec[u].push_back({{v, w}, vec[v].size()});
vec[v].push_back({{u, 0}, vec[u].size() - 1});
}
int ans = 0;
while (bfs())
{
int minn = 1e18;
for (int i = t; i != s; i = pre[i].v1)
{
minn = min(minn, pre[i].w1);
}
for (int i = t; i != s; i = pre[i].v1)
{
vec[pre[i].v1][pre[i].id].first.second -= minn;
vec[i][vec[pre[i].v1][pre[i].id].second].first.second += minn;
}
ans += minn;
}
cout << ans << endl;
}
复杂度:O(nm^2),但是远达不到。
模板题是 P3376。
dinic 算法
EK 算法还是太不牛了,一次只能找一个增广路,有没有什么算法一次找多个?
有的兄弟有的,Dinic 算法。
Dinic 算法利用了分层图。
在残量网络中,从源点 s 做 BFS,记录每个点 u 到 s 的最短距离 d_u(只走残量大于 0 的边)。这样把网络分为若干层:
第 0 层:s(d_s=0)
第 i 层:所有 d_u=i 的点
分层图只保留满足 d_v = d_u+1 的边 u \to v,即只走往下一层的边。
Dinic 在每个阶段只找最短的增广路,使得:
每个阶段所有增广路长度相同
一个阶段结束后,最短增广路长度必然增加
最短增广路长度最多增加 n-1 次,故最多 n 个阶段
复杂度 O(n^2m),与 EK 算法相同的:远达不到。
代码:
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[41000];
int de[41000], cu[41000], q[41000];
bool bfs()
{
for (int i = 0; i <= t; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= t; i++)
cu[i] = 0;
int d;
while ((d = dfs(s, 1e18)) != 0)
{
f += d;
}
}
return f;
}
signed main()
{
}
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m, s, t;
vector<pair<pair<int, int>, int>> vec[10005];
int vis[10005];
struct edge
{
int v1, w1, id;
} pre[10005];
bool bfs()
{
queue<int> que;
memset(vis, 0, sizeof(vis));
vis[s] = 1;
que.push(s);
while (!que.empty())
{
int v = que.front();
que.pop();
if (v == t)
return 1;
for (int i = 1; i < vec[v].size(); i++)
{
if (vec[v][i].first.second && !vis[vec[v][i].first.first])
{
vis[vec[v][i].first.first] = 1;
pre[vec[v][i].first.first].v1 = v;
pre[vec[v][i].first.first].w1 = vec[v][i].first.second;
pre[vec[v][i].first.first].id = i;
que.push(vec[v][i].first.first);
}
}
}
return 0;
}
signed main()
{
volatile char c1;
clock_t b = clock();
cin >> n >> m;
int sum = 0;
s = 0;
t = n + m + 1;
for (int i = 0; i <= t; i++)
vec[i].push_back({{0, 0}, 0});
for (int i = 1; i <= n; i++)
{
int w;
cin >> w;
sum += w;
vec[s].push_back({{i, w}, vec[i].size()});
vec[i].push_back({{s, 0}, vec[s].size() - 1});
}
for (int i = 1; i <= m; i++)
{
int x;
cin >> x;
int q = n + i;
vec[q].push_back({{t, 1}, vec[t].size()});
vec[t].push_back({{q, 0}, vec[q].size() - 1});
for (int j = 1; j <= x; j++)
{
int u;
cin >> u;
vec[u].push_back({{q, 1}, vec[q].size()});
vec[q].push_back({{u, 0}, vec[u].size() - 1});
}
}
int ans = 0;
while (bfs())
{
int minn = 1e18;
for (int i = t; i != s; i = pre[i].v1)
minn = min(minn, pre[i].w1);
for (int i = t; i != s; i = pre[i].v1)
{
vec[pre[i].v1][pre[i].id].first.second -= minn;
vec[i][vec[pre[i].v1][pre[i].id].second].first.second += minn;
}
ans += minn;
}
if (ans != sum)
cout << "No Solution!" << endl;
else
{
for (int i = 1; i <= n; i++)
{
cout << i << ":";
for (int j = 1; j < vec[i].size(); j++)
if (vec[i][j].first.second == 0 && vec[i][j].first.first > n && vec[i][j].first.first <= n + m)
cout << " " << vec[i][j].first.first - n;
cout << endl;
}
}
volatile char c2;
cerr << "Memory: " << abs(&c1 - &c2) / 1024 / 1024 << " MB" << endl;
clock_t e = clock();
cerr << "Time: " << (double)(e - b) * 1000 / CLOCKS_PER_SEC << " ms" << endl;
}
[POI 2005] KOS-Dicing
题意
## 思路
看到最大值最小一眼二分。
源点指向每场比赛表示每场比赛有且仅有一个人赢,每场比赛指向对应的两个玩家,若这条边的流是 $1$ 代表对应的玩家赢了,否则代表输了。
假设我们未卜先知,知道在赢得最多的人赢得最少的情况下赢得最多的人赢了 $x$ 把,则每个人赢得场数不超过 $x$,即对于每个人建一个边连向汇点,容量是 $x$,跑最大流,若最大流是 $m$,即每个比赛都有一个赢家。
考虑如何求出这个未卜先知的 $x$,显然可以二分求 $x$,那就做完了。
哦对还要输出方案,那就再跑一次最大流然后暴力查找对于每场比赛谁赢了就行。
注意有 SPJ,别管和样例一不一样。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t;
vector<pair<pair<int, int>, int>> vec[20005];
int de[20005], cu[20005], q[20005];
bool bfs()
{
for (int i = 0; i <= t; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 1; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
return d;
}
}
}
return 0;
}
int a[10005], b[10005];
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= t; i++)
cu[i] = 1;
int d;
while (d = dfs(s, 1e18))
f += d;
}
return f;
}
bool check(int x)
{
for (int i = 0; i <= n + m + 1; i++)
{
vec[i].clear();
vec[i].push_back({{0, 0}, 0});
}
s = 0;
t = n + m + 1;
for (int i = 1; i <= m; i++)
{
add(s, i, 1);
add(i, m + a[i], 1);
add(i, m + b[i], 1);
}
for (int i = 1; i <= n; i++)
{
add(m + i, t, x);
}
return dinic() >= m;
}
signed main()
{
volatile char c1;
clock_t b1 = clock();
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> a[i] >> b[i];
int l = 1, r = m, k = m;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid))
{
k = mid;
r = mid - 1;
}
else
l = mid + 1;
}
cout << k << endl;
check(k);
for (int i = 1; i <= m; i++)
{
int w = 0;
for (int j = 1; j < vec[i].size(); j++)
{
int to = vec[i][j].first.first;
int ca = vec[i][j].first.second;
if (to > m && to <= m + n && ca == 0)
{
w = to - m;
break;
}
}
cout << (w == a[i] ? 1 : 0) << "\n";
}
volatile char c2;
cerr << "Memory: " << abs(&c1 - &c2) / 1024 / 1024 << " MB" << endl;
clock_t e = clock();
cerr << "Time: " << (double)(e - b1) * 1000 / CLOCKS_PER_SEC << " ms" << endl;
}
```
## [\[清华集训 2012\] 最小生成树](https://www.luogu.com.cn/problem/P5934)
切了这题,就能进清华集训了吗?
## 题意
给一个图,问最少删几个边才能使得某个给定的边即在最小生成树又在最大生成树上。
## 思路
引理:如果一条插入边 $(u,v,L)$ 后该边可能在最小生成树上,那么如果将边权小于 $L$ 的边组成一个新图,则新图不连通。
证明:
假设命题不成立:$e$ 可能出现在某棵 MST 中,且 $G_{<L}$ 中 $u, v$ 连通。
由连通性,$G_{<L}$ 中存在 $u$-$v$ 路径 $P: u=x_0 \to x_1 \to \dots \to x_k=v$,且 $P$ 上每条边权值都严格小于 $L$。
在 $G'$ 中,新边 $e$ 与路径 $P$ 拼接构成一个环 $C: u \to x_1 \to \dots \to x_k=v \to u$。环上 $P$ 的边权都 $<L$,而 $w(e)=L$,故 $e$ 是环 $C$ 中严格最大的边。
由 MST 的环性质(环上严格最大的边不会出现在任何 MST 中),$e$ 不可能出现在 $G'$ 的任何 MST 中,与假设矛盾。
故假设不成立,$G_{<L}$ 中 $u, v$ 不连通,证毕。
同理,如果一条插入边 $(u,v,L)$ 后该边可能在最大生成树上,那么如果将边权大于 $L$ 的边组成一个新图,则新图不连通。
那不就是建两个图,第一个是只有所有边权 $<L$ 的边的图,一个是所有边权 $>L$ 的图,对于每个图求使它不联通的最小割吗?
由最小割等于最大流,对于两个图分别跑最大流相加即可。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[200005];
int de[200005], cu[200005], q[200005];
bool bfs()
{
for (int i = 0; i <= n; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= n; i++)
cu[i] = 0;
int d;
while (d = dfs(s, 1e18))
f += d;
}
return f;
}
void cl()
{
for (int i = 0; i <= n; i++)
vec[i].clear();
}
struct edge
{
int u, v, w;
} e[200005];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++)
cin >> e[i].u >> e[i].v >> e[i].w;
cin >> s >> t >> L;
for (int i = 1; i <= m; i++)
if (e[i].w < L)
{
add(e[i].u, e[i].v, 1);
add(e[i].v, e[i].u, 1);
}
int r1 = dinic();
for(int i = 1;i <= m;i++){
vec[i].clear();
}
cl();
for (int i = 1; i <= m; i++)
if (e[i].w > L)
{
add(e[i].u, e[i].v, 1);
add(e[i].v, e[i].u, 1);
}
int r2 = dinic();
cout << r1 + r2 << endl;
}
```
## [\[ABC239G\] Builder Takahashi](https://www.luogu.com.cn/problem/AT_abc239_g)
### 题意
给定一张 $n$ 个点 $m$ 条边的连通无向图,要求在某些点(不能为 $1$ 号点或者 $n$ 号点)设立障碍,在 $i$ 号点建立障碍的费用为 $c_i$,要使得 $1$ 号点和 $n$ 号点不连通,求最小花费的方案。
## 思路
这不还是让一张图不联通?上一题的弱化版,最小割等于最大流,秒了秒了。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[20005];
int de[20005], cu[20005], q[20005];
bool bfs()
{
for (int i = 0; i <= n; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= n; i++)
cu[i] = 0;
int d;
while (d = dfs(s, 1e18))
f += d;
}
return f;
}
int c[105], nn;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> nn >> m;
for (int i = 1; i <= m; i++)
{
int a, b;
cin >> a >> b;
add(a + nn, b, 1e18);
add(b + nn, a, 1e18);
}
for (int i = 1; i <= nn; i++)
cin >> c[i];
for (int i = 2; i <= nn - 1; i++)
{
add(i, i + nn, c[i]);
}
s = 1 + nn;
t = nn;
n = 2 * nn;
int r = dinic();
cout << r << "\n";
bfs();
vector<int> an;
for (int i = 2; i <= nn - 1; i++)
if (de[i] && !de[i + nn])
an.push_back(i);
cout << an.size() << "\n";
for (int i : an)
cout << i << " ";
if (an.empty())
cout << "\n";
}
```
## [方格取数问题](https://www.luogu.com.cn/problem/P2774)
这个题超级有意思!有六倍经验($2$ 紫 $4$ 蓝)。
## 题意
有一个 $m$ 行 $n$ 列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的和。
## 思路
考虑将网格黑板染色,先把源点连每个黑点,容量是点权,把每个黑点连周围的四个白的,容量没有意义,设为 inf,将每个白点连汇点,容量为点权,既然连了边的黑点白点只能留一个,那就是典型的断链,最小割即可。
最小割等于最大流,按上面的方法建图跑最大流就做完了。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[20005];
int de[20005], cu[20005], q[20005];
bool bfs()
{
for (int i = 0; i <= t; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= t; i++)
cu[i] = 0;
int d;
while ((d = dfs(s, 1e18)) != 0)
{
f += d;
}
}
return f;
}
int a[1000][1000], nn;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int sum = 0;
cin >> m >> n;
s = 0, t = n * m + n + 1;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
cin >> a[i][j];
sum += a[i][j];
}
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
int id = (i - 1) * n + j;
if ((i + j) & 1)
{
add(s, id, a[i][j]);
if (i > 1)
add(id, id - n, 0x3f3f3f3f);
if (i < m)
add(id, id + n, 0x3f3f3f3f);
if (j > 1)
add(id, id - 1, 0x3f3f3f3f);
if (j < n)
add(id, id + 1, 0x3f3f3f3f);
}
else
add(id, t, a[i][j]);
}
int fl = dinic();
cout << sum - fl << endl;
}
```
好了,讲一讲六倍经验吧!
### 二倍经验[王者之剑](https://www.luogu.com.cn/problem/P4474)
#### 题意
化简后题意和上一题一样,不再过多阐述。
#### 思路
和上一题完全一样。
代码:和上一题完全一样。
### 三倍经验[骑士放置](https://www.luogu.com.cn/problem/P10939)
#### 题意
一个网格,有一些地方不能放东西,问其他地方最多放多少骑士。
注:骑士的行走规则和象棋种的马相同。
#### 思路
将上一题的思路转化一下,黑格连白格变为每个点连若他是骑士可以跳到的点,然后做完了。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[20005];
int de[20005], cu[20005], q[20005];
bool bfs()
{
for (int i = 0; i <= t; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= t; i++)
cu[i] = 0;
int d;
while ((d = dfs(s, 1e18)) != 0)
{
f += d;
}
}
return f;
}
char a[105][105];
int dx[] = {1, 1, 2, 2, -1, -1, -2, -2};
int dy[] = {2, -2, 1, -1, 2, -2, 1, -1};
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int nn, mm, kk;
cin >> nn >> mm >> kk;
n = nn;
m = mm;
s = 0, t = n * m + m + 1;
for (int i = 1; i <= kk; i++)
{
int x, y;
cin >> x >> y;
a[x][y] = 1;
}
int cnt = nn * mm - kk;
for (int i = 1; i <= nn; i++)
for (int j = 1; j <= mm; j++)
{
if (a[i][j])
continue;
int id = (i - 1) * mm + j;
if ((i + j) & 1)
{
add(s, id, 1);
for (int k = 0; k < 8; k++)
{
int ni = i + dx[k], nj = j + dy[k];
if (ni < 1 || ni > nn || nj < 1 || nj > mm)
continue;
if (a[ni][nj])
continue;
add(id, (ni - 1) * mm + nj, 0x3f3f3f3f);
}
}
else
add(id, t, 1);
}
int fl = dinic();
cout << cnt - fl << endl;
}
```
### 四倍经验 [\[TJOI2013\] 攻击装置](https://www.luogu.com.cn/problem/P4304)
和上题一模一样,输入方式变了变,不再用过多篇幅阐述,代码也不放了。
### 五倍经验[骑士共存问题](https://www.luogu.com.cn/problem/P3355)
还是一样的,输入方式变了变。
### 六倍经验[长脖子鹿放置](https://www.luogu.com.cn/problem/P5030)
还是一样的,不过是改了改连边的坐标。
代码不放了。
## [太空飞行计划问题](https://www.luogu.com.cn/problem/P2762)
### 题意
每个实验有对应的奖金和需要的设备(多个),你可以选择做和不做,获得相应的收益,最大化奖金总和减去买设备的钱。
### 思路
所有的实验与源点相连,容量为其奖金,所有的器材与汇点相连,容量为其价格,中间实验与器材相连,容量为无穷大。无穷大时绝对割不掉的,要么割奖金,要么割设备,由于要求收益最大,所以是最小割,最小割等于最大流,跑最大流即可。
代码:
```
#include <bits/stdc++.h>
using namespace std;
#define add(u, v, w) \
vec[u].push_back({{v, w}, vec[v].size()}); \
vec[v].push_back({{u, 0}, vec[u].size() - 1});
#define int long long
int n, m, s, t, L;
vector<pair<pair<int, int>, int>> vec[20005];
int de[20005], cu[20005], q[20005];
bool bfs()
{
for (int i = 0; i <= t; i++)
de[i] = 0;
int he = 0, ta = 0;
q[ta++] = s;
de[s] = 1;
while (he < ta)
{
int v = q[he++];
for (int i = 0; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && !de[to])
{
de[to] = de[v] + 1;
q[ta++] = to;
}
}
}
return de[t] != 0;
}
int dfs(int v, int fl)
{
if (v == t)
return fl;
int re = 0;
for (int &i = cu[v]; i < (int)vec[v].size(); i++)
{
int to = vec[v][i].first.first;
int ca = vec[v][i].first.second;
if (ca && de[to] == de[v] + 1)
{
int d = dfs(to, min(fl - re, ca));
if (d > 0)
{
vec[v][i].first.second -= d;
vec[to][vec[v][i].second].first.second += d;
re += d;
if (re == fl)
return re;
}
}
}
return re;
}
int dinic()
{
int f = 0;
while (bfs())
{
for (int i = 0; i <= t; i++)
cu[i] = 0;
int d;
while ((d = dfs(s, 1e18)) != 0)
{
f += d;
}
}
return f;
}
char a[105][105];
int dx[] = {1, 1, 2, 2, -1, -1, -2, -2};
int dy[] = {2, -2, 1, -1, 2, -2, 1, -1};
signed main()
{
cin >> m >> n;
s = 0;
t = m + n + 1;
int sum = 0;
for (int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u;
sum += u;
add(s, i, u);
char tools[10000];
memset(tools, 0, sizeof tools);
cin.getline(tools, 10000);
int ulen = 0, tool;
while (sscanf(tools + ulen, "%d", &tool) == 1) // 之前已经用scanf读完了赞助商同意支付该实验的费用
{ // tool是该实验所需仪器的其中一个
// 这一行,你可以将读进来的编号进行储存、处理,如连边。
add(i, m + tool, 0x3f3f3f3f);
if (tool == 0)
ulen++;
else
{
while (tool)
{
tool /= 10;
ulen++;
}
}
ulen++;
}
}
for (int i = 1; i <= n; i++)
{
int jia;
cin >> jia;
add(m + i, t, jia);
}
int fl = dinic();
for (int i = 1; i <= m; i++)
{
if (de[i])
{
cout << i << " ";
}
}
cout << endl;
for (int i = 1; i <= n; i++)
{
if (de[i + m])
{
cout << i << " ";
}
}
cout << endl;
cout << sum - fl << endl;
}
```
---
关于我为什么写网络流的笔记:我线上听课,别人线下,然后我们分为两堂课有课间休息,第一堂老师没有共享屏幕,听了一节课老师的声音以感性理解,第二堂老师没有开麦,看了一节课老师在黑板上画乱七八糟的图案,啥也没听懂。