网络流 24 题
0.前言
开始学习网络流,争取用几天做完吧(挖坑了)
对于一些能用二分图做的尽量我也会写下代码。
1. 搭配飞行员
题目描述
飞行大队有若干个来自各地的驾驶员,专门驾驶一种型号的飞机,这种飞机每架有两个驾驶员,需一个正驾驶员和一个副驾驶员。由于种种原因,例如相互配合的问题,有些驾驶员不能在同一架飞机上飞行,问如何搭配驾驶员才能使出航的飞机最多。
因为驾驶工作分工严格,两个正驾驶员或两个副驾驶员都不能同机飞行。
输入格式
第一行,两个整数 与 ,表示共有 个飞行员,其中有 名飞行员是正驾驶员。
下面有若干行,每行有 个数字 、。表示正驾驶员 和副驾驶员 可以同机飞行。
注:正驾驶员的编号在前,即正驾驶员的编号小于副驾驶员的编号。
输出格式
仅一行一个整数,表示最大起飞的飞机数。
样例输入
10 5
1 7
2 6
2 10
3 7
4 8
5 9
样例输出
4
说都不用说了吧
二分图匹配或者网络流都可以做
二分图就直接匹配
网络流的话建一个源点向所有正飞行员每人连一条容量为1的边,再建一个汇点,所有副飞行员向其连一条容量为1的边,中间按题意连
二分图匹配代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5;
int n, m, to[N], nxt[N], lf[N], cnt, rt[N], adj[N];
bool vis[N];
inline void add(int u, int v) {
nxt[++cnt] = adj[u], adj[u] = cnt, to[cnt] = v;
}
inline bool dfs(int x) {
for (int i = adj[x]; i; i = nxt[i]) {
int v = to[i];
if (vis[v])continue;
vis[v] = 1;
if (!rt[v] || dfs(rt[v])) {
rt[v] = x, lf[x] = v;
return true;
}
}
return false;
}
int main() {
//freopen("in.txt", "r", stdin);
cin >> n >> m;
int u, v;
memset(vis, 0, sizeof(vis));
while (cin >> u >> v) {
add(u, v);
}
for (int i = 1; i <= m; ++i)dfs(i);
int ans = 0;
for (int i = 1; i <= m; ++i)
if (lf[i] != 0)
ans++;
cout << ans << endl;
}
网络流写法
#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 20010, inf = 1e8;
int h[N], to[M], nxt[M], c[M], tot = 1;
int n, m, s, t;
int d[N], now[N];
queue<int> q;
inline void add(int a, int b, int w) {
to[++tot] = b;
c[tot] = w;
nxt[tot] = h[a];
h[a] = tot;
}
bool bfs() {
memset(d, 0, sizeof d);
d[s] = 1;
now[s] = h[s];
while (!q.empty()) q.pop();
q.push(s);
while (!q.empty()) {
int x = q.front();
q.pop();
for (int i = h[x], v; v = to[i], i; i = nxt[i]) {
if (c[i] && !d[v]) {
d[v] = d[x] + 1;
now[v] = h[v];
if (v == t)
return true;
q.push(v);
}
}
}
return false;
}
int find(int x, int flow) {
if (x == t)
return flow;
int rest = flow, k = 0;
for (int i = now[x], v; v = to[i], i && rest; i = nxt[i]) {
if (c[i] && d[v] == d[x] + 1) {
now[x] = i;
k = find(v, min(rest, c[i]));
if (!k)
d[v] = 0;
c[i] -= k;
c[i ^ 1] += k;
rest -= k;
}
}
return flow - rest;
}
int dinic(int s, int t) {
int maxflow = 0, flow = 0;
while (bfs())
while (flow = find(s, inf)) maxflow += flow;
return maxflow;
}
int main() {
cin >> n >> m;
int u, v;
while (cin >> u >> v) {
if (u == -1 && v == -1)
break;
add(u, v, 1);
add(v, u, 0);
}
s = 0, t = n + 1;
for (int i = 1; i <= m; i++) add(s, i, 1), add(i, s, 0);
for (int i = m + 1; i <= n; i++) add(i, t, 1), add(t, i, 0);
cout << dinic(s, t) << endl;
return 0;
}
2. 太空飞行计划
题目描述
W 教授正在为国家航天中心计划一系列的太空飞行。每次太空飞行可进行一系列商业性实验而获取利润。现已确定了一个可供选择的实验集合 ,和进行这些实验需要使用的全部仪器的集合 。实验 需要用到的仪器是 的子集 。
配置仪器 的费用为 美元。实验 的赞助商已同意为该实验结果支付 美元。W 教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大。这里净收益是指进行实验所获得的全部收入与配置仪器的全部费用的差额。
对于给定的实验和仪器配置情况,编程找出净收益最大的试验计划。
输入格式
第 行有 个正整数 和 。 是实验数, 是仪器数。接下来的 行,每行是一个实验的有关数据。第一个数赞助商同意支付该实验的费用;接着是该实验需要用到的若干仪器的编号。最后一行的 个数是配置每个仪器的费用。
输出格式
第 行是实验编号,第 行是仪器编号,最后一行是净收益。
请不要输出行末空格以免被判答案错误!
样例输入
2 3
10 1 2
25 2 3
5 6 7
样例输出
1 2
1 2 3
17
题意:有m个太空飞行计划,执行一个计划可以获得xi的赞助,但是每个飞行计划需要一些仪器。每个仪器配置需要花费xi。求执行一些天空飞行计划使得收益最大。
思路:最大权闭合图。最大权闭合图问题,可以转化成最小割问题,进而用最大流解决。
转载一篇苣苣的博客:最大权闭合图
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define maxn 205
#define maxm 10005
bool flag;
int m, n, s, t, answer, count, total = 1, cur[maxn], head[maxn], to[maxm], next[maxm], weight[maxm];
int a[maxm], dis[maxn], flow[maxn];
inline int read() {
int ch = getchar(), num = 0;
while (ch < 48 || ch > 57) ch = getchar();
while (ch >= 48 && ch <= 57) num = (num << 3) + (num << 1) + (ch ^ 48), ch = getchar();
if (ch == '\r' || ch == '\n')
flag = true;
else
flag = false;
return num;
}
inline void add(int a, int b, int c) {
to[++total] = b;
next[total] = head[a];
weight[total] = c;
head[a] = total;
}
inline bool bfs() {
memset(dis, 0, sizeof(dis));
dis[s] = 1;
std::queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = next[i]) {
int v = to[i];
if (dis[v] || !weight[i])
continue;
dis[v] = dis[u] + 1;
q.push(v);
}
}
for (int i = s; i <= t; i++) cur[i] = head[i];
return dis[t];
}
int dfs(int u, int flow) {
if (u == t)
return flow;
int sum = flow;
for (int i = cur[u]; i; i = next[i]) {
cur[u] = i;
int v = to[i];
if (dis[v] != dis[u] + 1 || !weight[i])
continue;
int f = dfs(v, std::min(weight[i], sum));
weight[i] -= f;
weight[i ^ 1] += f;
sum -= f;
if (!sum)
break;
}
return flow - sum;
}
int main() {
m = read(), n = read(), s = 0, t = m + n + 1;
for (int i = 1; i <= m; i++) {
count += (a[i] = read());
add(s, i, a[i]);
add(i, s, 0);
while (true) {
int a = read();
add(i, a + m, inf);
add(a + m, i, 0);
if (flag)
break;
}
}
for (int i = 1; i <= n; i++) {
int a = read();
add(i + m, t, a);
add(t, i + m, 0);
}
while (bfs()) answer += dfs(s, inf);
bool flag = true;
for (int i = 1; i <= m; i++)
if (dis[i]) {
if (flag)
flag = false;
else
putchar(' ');
printf("%i", i);
}
putchar('\n');
flag = true;
for (int i = m + 1; i <= m + n; i++)
if (dis[i]) {
if (flag)
flag = false;
else
putchar(' ');
printf("%i", i - m);
}
putchar('\n');
printf("%i", count - answer);
return 0;
}