网络流 24 题汇总(LOJ 上只有 22 题???)
太裸的我就不放代码了。。。(黑体字序号的题表示值得注意)
1、搭配飞行员 [LOJ#6000]
二分图最大匹配。
2、太空飞行计划 [LOJ#6001]
最小割常规套路、输出方案。(注:这题换行符要用 \r
)
3、最小路径覆盖 [LOJ#6002]
网上大多数题解都是二分图相关的,但这题有一个更直观的做法。
我们限制每个点的流量上下界都为 \(1\),从源点向每个点的“入点”连容量为 \(1\) 的边,从每个点的“出点”向汇点连容量为 \(1\) 的边,然后跑最小流。
最后输出方案暴力 dfs 即可。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
}
#define maxn 410
#define maxm 136010
#define oo 2147483647
int at[maxn];
int CntP;
struct Point {
int id;
Point(): id(0) {}
int p() { return id ? id : id = ++CntP; }
} In[maxn], Out[maxn], S, T, SS, TT;
struct Edge {
int from, to, flow;
Edge() {}
Edge(int _1, int _2, int _3): from(_1), to(_2), flow(_3) {}
};
struct Dinic {
int n, m, s, t, head[maxn], nxt[maxm];
Edge es[maxm];
int Q[maxn], hd, tl, vis[maxn];
int cur[maxn];
bool CanPass, etag[maxm];
int cntp, path[maxn];
void init() {
m = 0; memset(head, -1, sizeof(head));
return ;
}
void setn(int _) {
n = _;
return ;
}
void AddEdge(int a, int b, int c) {
es[m] = Edge(a, b, c); nxt[m] = head[a]; head[a] = m++;
es[m] = Edge(b, a, 0); nxt[m] = head[b]; head[b] = m++;
return ;
}
bool BFS() {
memset(vis, 0, sizeof(vis));
vis[t] = 1;
hd = tl = 0; Q[++tl] = t;
while(hd < tl) {
int u = Q[++hd];
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i^1];
if(!CanPass && etag[i]) continue;
if(!vis[e.from] && e.flow) {
vis[e.from] = vis[u] + 1;
Q[++tl] = e.from;
}
}
}
return vis[s] > 1;
}
int DFS(int u, int a) {
if(u == t || !a) return a;
int flow = 0, f;
for(int& i = cur[u]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(!CanPass && etag[i]) continue;
if(vis[e.to] == vis[u] - 1 && (f = DFS(e.to, min(a, e.flow)))) {
flow += f; a -= f;
e.flow -= f; es[i^1].flow += f;
if(!a) return flow;
}
}
return flow;
}
int MaxFlow(int _s, int _t, bool canpass) {
s = _s; t = _t; CanPass = canpass;
int flow = 0;
while(BFS()) {
rep(i, 1, n) cur[i] = head[i];
flow += DFS(s, oo);
}
return flow;
}
void getpath(int u) {
path[++cntp] = at[u];
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(e.flow < oo && at[e.to]) getpath(Out[at[e.to]].p());
}
return ;
}
void print(int s) {
int cnt = 0;
for(int i = head[s]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(!e.flow && e.to != t) {
cnt++;
cntp = 0; getpath(Out[at[e.to]].p());
rep(i, 1, cntp) printf("%d%c", path[i], i < cntp ? ' ' : '\n');
}
}
printf("%d\n", cnt);
return ;
}
} sol;
int main() {
int n = read(), m = read();
sol.init();
rep(i, 1, n)
sol.AddEdge(SS.p(), Out[i].p(), 1), sol.AddEdge(In[i].p(), TT.p(), 1),
sol.AddEdge(S.p(), In[i].p(), 1), sol.AddEdge(Out[i].p(), T.p(), 1),
at[In[i].p()] = at[Out[i].p()] = i;
sol.etag[sol.m] = sol.etag[sol.m^1] = 1;
sol.AddEdge(T.p(), S.p(), oo);
rep(i, 1, m) {
int a = read(), b = read();
sol.AddEdge(Out[a].p(), In[b].p(), oo);
}
sol.setn(CntP);
sol.MaxFlow(SS.p(), TT.p(), 1);
sol.MaxFlow(T.p(), S.p(), 0);
sol.print(S.p());
return 0;
}
4、魔术球 [LOJ#6003]
二分答案(答案不超过 \(2000\)),然后和上一题基本一样。(注:这题不能用 fread()
)
5、圆桌聚餐 [LOJ#6004]
二分图匹配、输出方案。
6、最长递增子序列 [LOJ#6005]
先 dp 出 \(f_i\) 表示以第 \(i\) 个数结尾的最长不降子序列长度,然后对于 \(∀(i, j)\) 满足 \(i < j, f_i + 1 = f_j, A_i \le A_j\),在第 \(i\) 个数和第 \(j\) 个数之间连边,然后点限制流量为 \(1\)(第三问把第一个和第 \(n\) 个节点的流量限制改成无穷),跑最大流。
7、试题库 [LOJ#6006]
同第 \(5\) 题。注意这题有可能有某个类型须要 \(0\) 道题。
8、方格取数 [LOJ#6007]
将棋盘黑白染色后跑最小割。
9、餐巾计划 [LOJ#6008]
有下界的最小费用可行流,详见这里(网上许多做法似乎没这个直观,当然他们是做了优化)。
10、数字梯形 [LOJ#6010]
最小费用最大流裸题。
11、运输问题 [LOJ#6011]
最小费用最大流裸题。
12、分配问题 [LOJ#6012]
同上。
13、负载平衡 [LOJ#6013]
小于平均值的仓库向汇点连容量为平均值减仓库存量的边,源点向大于平均值的仓库连容量为库存减平均值的边(费用均为 \(0\)),然后相邻仓库连容量无穷费用为 \(1\) 的双向边。最后跑最小费用最大流。
14、最长 k 可重区间集 [LOJ#6014]
这个题有点厉害啊,想了半天。。。
还是有一个直接的思路。把坐标离散化后,最后的收益相当于每段小区间的长度乘它被覆盖的次数的总和。对于一个给出的开区间,我们可以用“环”的性质保证它出了 \(1\),最后能够回来 \(1\)。就是说我们将所有点从左到右依次连边(容量为 \(k\),费用为负的区间长度,这里的区间长度就是它和下一个点的距离),然后对于一个区间 \((l, r)\),连一条从点 \(r\) 到点 \(l\),容量为 \(1\),费用为 \(0\) 的边,最后跑最小费用可行流。(注意这个图是有负环的,我们可以先强行流满所有的负边,然后用类似有下界可行流的办法进行调整)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define maxn 1510
#define maxm 3014
#define oo 2147483647
struct Edge {
int from, to, flow, cost;
Edge() {}
Edge(int _1, int _2, int _3, int _4): from(_1), to(_2), flow(_3), cost(_4) {}
};
struct ZKW {
int n, m, s, t, cost, ans, head[maxn], nxt[maxm];
Edge es[maxm];
bool inq[maxn];
int d[maxn], Q[maxn*10], hd, tl;
bool vis[maxn];
void init() {
m = 0; memset(head, -1, sizeof(head));
return ;
}
void setn(int _) {
n = _;
return ;
}
void AddEdge(int a, int b, int c, int d) {
es[m] = Edge(a, b, c, d); nxt[m] = head[a]; head[a] = m++;
es[m] = Edge(b, a, 0, -d); nxt[m] = head[b]; head[b] = m++;
return ;
}
bool BFS() {
rep(i, 1, n) d[i] = oo;
d[t] = 0;
hd = tl = 0; Q[++tl] = t; inq[t] = 1;
while(hd < tl) {
int u = Q[++hd]; inq[u] = 0;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i^1];
if(d[e.from] > d[u] + e.cost && e.flow) {
d[e.from] = d[u] + e.cost;
if(!inq[e.from]) inq[e.from] = 1, Q[++tl] = e.from;
}
}
}
if(d[s] == oo) return 0;
cost = d[s];
return 1;
}
int DFS(int u, int a) {
if(u == t || !a) return ans += cost * a, a;
if(vis[u]) return 0;
vis[u] = 1;
int flow = 0, f;
for(int i = head[u]; i != -1; i = nxt[i]) {
Edge& e = es[i];
if(d[e.to] == d[u] - e.cost && (f = DFS(e.to, min(a, e.flow)))) {
flow += f; a -= f;
e.flow -= f; es[i^1].flow += f;
if(!a) return flow;
}
}
return flow;
}
int MaxFlow(int _s, int _t) {
s = _s; t = _t;
int flow = 0, f;
while(BFS())
do {
memset(vis, 0, sizeof(vis));
f = DFS(s, oo);
flow += f;
} while(f);
return flow;
}
} sol;
#define pii pair <int, int>
#define x first
#define y second
#define mp(x, y) make_pair(x, y)
pair <int, int> line[maxn];
int num[maxn], cntn;
int main() {
int n = read(), k = read();
rep(i, 1, n) {
int l = read(), r = read();
if(l > r) swap(l, r);
line[i] = mp(l, r);
num[++cntn] = l; num[++cntn] = r;
}
sort(num + 1, num + cntn + 1);
cntn = unique(num + 1, num + cntn + 1) - num - 1;
rep(i, 1, n)
line[i].x = lower_bound(num + 1, num + cntn + 1, line[i].x) - num,
line[i].y = lower_bound(num + 1, num + cntn + 1, line[i].y) - num;
int S = cntn + n + 1, T = S + 1, sum = (num[cntn] - num[1]) * k;
sol.init(); sol.setn(T);
sol.AddEdge(S, cntn, k, 0); sol.AddEdge(1, T, k, 0);
rep(i, 2, cntn) sol.AddEdge(i, i - 1, k, num[i] - num[i-1]);
rep(i, 1, n) sol.AddEdge(line[i].y, line[i].x, 1, 0);
sol.MaxFlow(S, T);
printf("%d\n", sum - sol.ans);
return 0;
}
15、星际转移 [LOJ#6015]
这题其实应该给出一个条件:天数不会超过 \(30\)。那就枚举答案然后分层建图网络流检验。
16、孤岛营救问题 [LOJ#6121]
这题网络流怎么做?!
bfs 做法显然。令状态 \((x, y, s)\) 表示身处 \((x, y)\) 格子,当前有的钥匙集合为 \(s\),然后就搜就行了。
17、航空路线问题 [LOJ#6122]
\(2\) 到 \(n - 1\) 号城市限流为 \(1\),\(1\) 和 \(n\) 号城市限流为 \(2\),所有城市费用为 \(-1\),跑最小费用最大流,若最大流为 \(2\) 则有界,沿着流输出。
18、汽车加油行驶问题 [LOJ#6223]
这题也许是告诉我们费用流可以用来跑最短路吧。。。
令状态 \((x, y, k)\) 表示在坐标 \((x, y)\) 处,当前有 \(k\) 的油量。跑最短路即可。
19、深海机器人问题 [LOJ#6224]
最小费用最大流裸题。
20、火星探险问题 [LOJ#6225]
最小费用最大流裸题 + 输出方案。
21、骑士共存问题 [LOJ#6226]
同样是棋盘染色 + 最小割。
22、最长k可重线段集问题 [LOJ#6227]
这题和第 \(14\) 题很像,做法也一样,但是区间的权值变化了,不难处理。
网络流 24 题汇总(LOJ 上只有 22 题???)的更多相关文章
- 网络流基础&网络流24题
网络最大流 dinic+当前弧优化. const int N=10007,M=100007,inf=1e9; int s,t,head[N],ver[M],edge[M],Next[M],tot=1, ...
- 【刷题】LOJ 6227 「网络流 24 题」最长k可重线段集问题
题目描述 给定平面 \(\text{xoy}\) 上 \(n\) 个开线段组成的集合 \(\text{I}\) ,和一个正整数 \(k\) ,试设计一个算法. 从开线段集合 \(\text{I}\) ...
- loj #6014. 「网络流 24 题」最长 k 可重区间集
#6014. 「网络流 24 题」最长 k 可重区间集 题目描述 给定实直线 L LL 上 n nn 个开区间组成的集合 I II,和一个正整数 k kk,试设计一个算法,从开区间集合 I II 中选 ...
- loj #6013. 「网络流 24 题」负载平衡
#6013. 「网络流 24 题」负载平衡 题目描述 G 公司有 n nn 个沿铁路运输线环形排列的仓库,每个仓库存储的货物数量不等.如何用最少搬运量可以使 n nn 个仓库的库存数量相同.搬运货物时 ...
- [loj #6003]「网络流 24 题」魔术球 二分图最小路径覆盖,网络流
#6003. 「网络流 24 题」魔术球 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 ...
- [luogu_P1251][LOJ#6008]「网络流 24 题」餐巾计划
[luogu_P1251][LOJ#6008]「网络流 24 题」餐巾计划 试题描述 一个餐厅在相继的 \(N\) 天里,第 \(i\) 天需要 \(R_i\) 块餐巾 \((i=l,2,-,N)\) ...
- [LOJ#6002]「网络流 24 题」最小路径覆盖
[LOJ#6002]「网络流 24 题」最小路径覆盖 试题描述 给定有向图 G=(V,E).设 P 是 G 的一个简单路(顶点不相交)的集合.如果 V 中每个顶点恰好在 P 的一条路上,则称 P 是 ...
- loj #6122. 「网络流 24 题」航空路线问题
#6122. 「网络流 24 题」航空路线问题 题目描述 给定一张航空图,图中顶点代表城市,边代表两个城市间的直通航线.现要求找出一条满足下述限制条件的且途经城市最多的旅行路线. 从最西端城市出发,单 ...
- LOJ #6010. 「网络流 24 题」数字梯形
#6010. 「网络流 24 题」数字梯形 题目描述 给定一个由 n nn 行数字组成的数字梯形如下图所示.梯形的第一行有 m mm 个数字.从梯形的顶部的 m mm 个数字开始,在每个数字处可以 ...
随机推荐
- mongostat查看mongodb运行状态使用命令介绍
mongostat是mongodb自带的一个用来查看mongodb运行状态的工具 使用说明 mongostat -h 字段说明 启用后的状况是这样的 insert query update del ...
- js 封装父页面子页面交互接口
定义标准接口 Interface= {}; Interface.ParentWin = {}; Interface.ChildWin = {}; /** * 父页面提供的标准接口函数名称 */ Int ...
- kubernetes-ingress(十)
ingress https://kubernetes.io/docs/concepts/services-networking/ingress/ pod与ingress的关系 •通过label-sel ...
- tomcat服务器用Servlet类查找磁盘文件上的Json信息,如果匹配则在浏览器上显示出该条内容的全部信息
package com.swift; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOE ...
- C++的XML编程经验――LIBXML2库使用指南
C++的XML编程经验――LIBXML2库使用指南 写这篇文章的原因有如下几点:1)C++标准库中没有操作XML的方法,用C++操作XML文件必须熟悉一种函数库,LIBXML2是其中一种很优秀的XML ...
- Linux-准备工作
首先安装一个box,安装一个centos7,然后就是xshell,接下来就是 查看ip ifconfig ip addr vi /etc/sysconfig/network-scripts/ifcfg ...
- 汇编 if else 例子
.text .global _start _start: mov r1,#1 mov r2,#2 cmp r1, r2 bgt branch1 add r3,r1,r2 b end branc ...
- Python 正则表达式 匹配任意字符
.(句点)匹配除了换行之外的所有一个字符, .*(点-星)匹配除了换行外的所有字符 >>> >>> r=re.compile(r'.*')>>> ...
- 04.VUE学习之v-text v-html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...
- day 71 Django基础六之ORM中的锁和事务
Django基础六之ORM中的锁和事务 本节目录 一 锁 二 事务 三 xxx 四 xxx 五 xxx 六 xxx 七 xxx 八 xxx 一 锁 行级锁 select_for_update(no ...