网络流24题 解题报告
网络流这种重在建模的东西,多思考、多作总结总是好的罢
最短路
1.软件补丁问题
总共最多 \(20\) 个错误,压成 \(2^{20}\) 种状态直接跑最短路即可。
2.孤岛营救问题
总共最多 \(10\) 种钥匙,不难想到状压成 \(2^{10}\) 种状态 \(k\)。用 \((x,y,k)\) 表示当前坐标 \((x,y)\),钥匙的状态是 \(k\),总共 \(O(nm\times 2^P)\) 个结点,仍然是最短路。
注意一个房间可以有多个钥匙,每种钥匙拿到之后可以用无限次。
3.汽车加油行驶问题
发现最多只能行驶 \(k\) 条网格边这个限制很难表示。我们把它转化成汽车的油量,初始有 \(k\) 点,每行驶一条边就扣除一点,然后把图分层,用 \((x,y,k)\) 表示当前坐标 \((x,y)\),剩余油量 \(k\),总共 \(O(n^2K)\) 个结点,依旧是最短路。
汽车的行驶、遇到油库加油都很好表示。对于增设油库这个操作,由贪心的思想可以知道仅在油量为 \(0\) 的时候会使用(因为行驶的路径不会出现环,增设的油库只会用一次,所以增设的位置无关紧要,在还有油的时候没必要增设油库,说不准开着开着就碰到一个已有的油库了呢)。不难连边。
最大流
4.飞行员配对方案问题
二分图最大匹配,从源点向左侧的每个点连边,原图的边保留,从右侧的每个点向汇点连边。容量均为 \(1\)。一个流量即代表一个匹配,在源点和汇点处限流保证了每个点只会被匹配一次。对原图的每条边判断是否有流量即可,方法为在 dinic
跑出的残量网络上判断其反向边是否有流量。
5.圆桌问题
每张餐桌只能容纳一定数量的代表就餐,完美符合网络流的特点。我们用一个流量代表一个代表(?,从源点向每个单位提供 \(r_i\) 的流量,每个单位向每个餐桌连容量为 \(1\) 的边以体现出同一个餐桌上只能有一个同单位代表,最后在餐桌上向汇点限流 \(c_i\)。求最大流,对于每个单位连向每个餐桌的每条边,判断其是否有流量即可。
6.试题库问题
和上一题差不多,只不过每一题只有一点流量。由题目向所属类别连边即可。
7.骑士共存问题
对于每个格子向其周围 骑士能攻击到的八个格子连边(忽视掉有障碍的格子)。不难发现没有奇环,问题转化为二分图最大独立集。
要表示出一条边上的两个点最多只能选一个,直观的想法是从边入手限流:对于每条边设立一个结点,从这条边连接的两个点向这个结点连边,这个结点再向汇点连边。
但是在这题我们换一种做法。从另一个角度思考,我们把结点变成边,不选一个结点相当于割掉一条边:保留原图的边,权值 \(\inf\);源点向左侧连边,右侧向汇点连边,权值 \(1\)。对该图求最小割:原图的边权值 \(\inf\),不可能被割掉;割掉源点连向左侧点的边,流量便不会经它之手流到右侧后被汇点接收,右边与之相邻的点便可以放心选择;右侧点的对应边同理。最小割即在源点与汇点间不存在通路的前提下割掉边的最小权值和,因此总点数 \(-\) 最小割即为答案。
8.方格取数问题
与上一题几乎一模一样,只不过加上了点权。如果用上一题的第一种做法,我们将不得不加上费用以表示点权;如果用第二种方法,我们修改一下每个点与源点/汇点间的边的容量即可,割掉这条边的代价变成了这个点的点权。点权和 \(-\) 最小割即可。
9.太空飞行计划问题
发现和前两题很像:前两题是 选了一件东西就不能选与之相连的一些东西;本题是 选了一件东西就必须选与之相连的一些东西。借鉴前两题的思路,我们建出二分图,从实验向所需仪器连边,权值 \(\inf\);源点向实验连边,权值为实验收益。
现在我们面临一个问题:怎么处理仪器。为了得到实验的收益,我们不得不损失与之关联的仪器的费用;上一题,为了得到某些方格的收益,我们不得不损失与之关联的方格的收益。非常的像不是吗?反过来想,我们试着扭转所有事情:假定购买仪器可以赚钱,选了一个实验就不能赚到购买与实验关联仪器的钱。于是,我们从仪器向汇点连边,权值为仪器花费。于是,我们得到了一个完整的图,所以我们直接把所有事情扭转回去(即买东西又要花钱了)。
此时,割掉仪器到汇点的边权值为 \(w\) 的边,意义为 选 这个仪器,失去 \(w\) 的金钱。直观地,在上一题,割掉右侧点到汇点的边,得到的收益从 \(w\to 0\);本题,割掉右侧点到汇点的边,得到的收益从 \(0\to -w\)。此时最小割的意义,即让流量走不通 \(\text{源点}-\text{实验}-\text{仪器}-\text{汇点}\) 的路,即不能选了一个实验(\(\text{源点}-\text{实验}\) 的边未被切断),同时不选这个实验连向的器材(\(\text{器材}-\text{汇点}\) 的边未被切断)。
\(\text{实验}-\text{器材}\) 的边不会被切断。所有实验收益和 \(-\) 最小割即可。
10.最长不下降子序列问题
令 \(f_i\) 表示以第 \(i\) 个元素结尾的最长不下降子序列的长度,跑一个 \(\Theta(n^2)\) DP
求出 \(f\),\(s=\max\{f_i\}\)。回答第一问。
考虑如何用网络流表示这个东西。我们可以给序列的每个元素提供流量,让一个流量代表不下降子序列,从前往后一个一个地经过选出子序列的所有元素,最后流向汇点。问题来了,怎么保证所有流量流经的都是最长不下降子序列呢?我们把它拆成三个条件(构造序列时使用的的下标为 \(a_1,\cdots,a_s\)):
我们从源点仅向 \(\{i:f_i=1\}\) 连边,汇点同理,下标 \(i,j\) 间只有 \(f_j=f_i+1\) 且 \(a_j\ge a_i\) 时连边。容量均为 \(\inf\)。
考虑每个元素仅允许使用一次这个限制,发现其实际上就是在限流。如果我们把数列的元素变成边,就可以利用容量达到限流的目的。我们可以拆点,把一个下标拆成 入点 和 出点,把所有连向它的边接上它的入点,它的所有出边均从出点出发。入点和出点间连流量为 \(1\) 的边即可。这样我们就答出了第二问。
对于第三问,我们直接解除下标 \(1\) 和 \(n\) 的封印流量限制,即把它们入点和出点之间边的容量变为 \(\inf\)。发现这个东西会出锅,当且仅当 \(s=1\),此时流量会从 \(1\) 和 \(n\) 的入点进去后直接从出点出来,达到 \(\inf\) 的流量。特判一下即可。
11.最小路径覆盖问题
直观的想法是像上一题那样,拆点后直接用流量表示路径。发现这个想法会挂掉,原因是我们不能判断什么时候要从源点向某个入点提供流量,什么时候要让流量从某个出点流向汇点。
正♂男♂则♂反。观察样例:
图中带♂的边被我们选中构成最小路径覆盖。借助并查集的思想,我们把每个结点视作一个集合,每个带♂的边相当于将两个点的所在集合合并,使总集合数 \(-1\)。我们求出带♂边的个数 \(s\),最终只有 \(n-s\) 个集合,即原图的最小路径覆盖是 \(n-s\)。
考虑用网络流表示。发现一条路径的起点和终点被合并了一次,其它结点却被合并了两次,我们不知道哪些点可以作为起点和终点。拆点之后就没那么麻烦了。考虑用一点流量表示选中一个带♂的边,我们希望流量之间互不干扰,所以可以让流量走 \(\text{源点}-\text{出点}-\text{入点}-\text{汇点}\) 的路。从源点连向所有出点,保留原图 \(\text{出点}-\text{入点}\) 的边,从所有入点连向源点,容量均为 \(1\)。在入点和出点处限流旨在让每个入点和出点最多只对应一条♂。
求最大流,发现起点的特征是没有有流量的边连向其入点。把建出的图过一遍,处理出所有起点,从起点开始往下遍历整条路径即可。
注意这个方法仅对 DAG 有效。在有环的情况下,如果出现环覆盖,终点的出点会和起点的入点连起来,通过一点流量,即我们会把原图终点连向起点没有♂的边误判为带♂。
12.魔术球问题
在 \(i,j:i<j,\sqrt{i+j}\in \mathbb{Z}\) 间连边,明显就是最小路径覆盖。二分答案(可以证明答案 \(=O(n^2)\)),或者一个一个地加点、连边然后在残余网络上跑最大流并加上新的流量。前者显然更快。
13.星际转移问题
发现很难直接计算时间:随着时间推移,图的边集会不停地发生变化。这还玩个锤子啊。如果能把每个时间点的图分开讨论就好了。
稍加思考,这不就是分层图吗?实现也特别容易,以一点流量表示一个人,用 \((x,t)\) 表示一个结点。源点连向最初的地球 \((0,0)\),每一时间点的月球 \((-1,t)\) 都连向汇点;每个时间点飞船的路线是确定的,从 \((u,t)\) 连向 \((v,t+1)\);人可以待在地球和每个太空站不动,从 \((x,t)\) 连向 \((x,t+1)\)。像上题一样二分答案或者一个一个的加点、加边跑最大流即可。
最小/最大费用最大流
14.分配问题
裸题,流量表示分配一个人做一个工作,费用即收益,源点向每个人提供一点流量,工作向汇点限流 \(1\),人向工作连边,限流 \(1\) 、费用收益。分别跑一次最小、最大费用最大流即可。
15.运输问题
上题基础上修改一下源点向仓库、零售商店到汇点的容量即可。
16.负载平衡问题
令 \(s=\frac{\sum_{i=1}^n a_i}{n}\)。从最后的状态入手,每个仓库的库存量都为 \(s\)。自然想到用流量表示货物,费用表示搬运次数。源点给每个仓库 \(a_i\) 流量,相邻仓库间连双向边,不限流费用 \(1\),最后仓库向汇点限流 \(s\) 即可。
17.深海机器人问题
直接建出题目给的网格图,标本的价值体现于网格图边上的费用。很明显用流量表示机器人,源点为每个出发点提供一定流量,每个目的地向汇点限流,机器人从源点出发带着收集费用的使命在网格图上跑最后到达汇点。为了体现出标本只能被采集一次,我们把每条边拆开,一条限流 \(1\),有费用;一条不限流,没有费用。因为只能向北或向东移动,所以没有环;为了达到最大费用,流量会优先走有费用的边,费用最多只会被拿到一次。完美还原。
18.火星探险问题
上题基础上,忽视掉有障碍的点。费用从边转移到了点上,所以拆点,在入点和出点间像上题那样连边。对于输出路径,我们跑 \(n\) 次,每次从源点开始顺着有流量的边一路跑到汇点,顺便把路上的流量全部扣掉 \(1\) 即可。
19.数字梯形问题
发现这题的边相交即出现重边,我们分别在点上、边上限流即可。具体地,流量表示路径,拆点,入点和出点间的边限流 \(1\) 并加上费用,源点连向第一层的所有入点,容量 \(1\),最后一层的所有出点向汇点连边,容量 \(\inf\)。跑最大费用最大流回答第一问。对于二、三问,一点一点地杰除封印即可:第二问取消入点与出点间的流量限制;第三问继续取消梯形内部边的流量限制。到最后,只有源点连向第一层的边起到限流作用。
20.航空路线问题
设最西边的点为点 \(1\),最东边的点为点 \(n\)。拆点,直观思路是让流量表示航空路线在图上跑一圈然后回到源点。看起来可以实现,我们可以在点 \(n\) 的入点和出点间放 \(\inf\) 的费用吸引流量。但是,由于方向的不确定性,我们需要在一个点的入点和出点间连双向边,并且都要加上 \(1\) 的费用以统计城市数,这直接导致我们在求最大费用最大流时出现正环从而爆炸。
考虑如何把双向边扔掉只建单向边。发现从点 \(n\) 回来的路可以直接翻转,变成从点 \(1\) 飞向点 \(n\) 的路,问题转换成找到两条不在顶点处相交的从 \(1\) 到 \(n\) 的路径,从西向东连边即可。因为要有两条路径,我们开大点 \(1\) 和点 \(n\) 入点和出点间的限流为 \(2\),其它点的限流仍为 \(1\),原图的边不作限流。源点连向 \(1\) 的入点,\(n\) 的出点连向汇点,跑最大费用最大流。
如果最大流不足 \(2\),No Solution!
;否则,输出最大费用 \(-2\)(点 \(1\) 和点 \(n\) 各被重复计算了一次),从源点跑两次 dfs
,顺着有流量的边往下走,注意第二次需要逆序遍历,回溯时输出即可。
21.最长k可重区间集问题
数据范围 \(1\le l,r\le 10^5\)。没有这个也没关系,离散化一下就好了。我们只考虑数轴上的整点。令 \(N=\max\{r\}\),以费用表示区间长度。
自然想到每个线段向所覆盖的所有点连边,每个点再向汇点限流。问题是,每个区间一旦选中,就必须要雨露均沾地把流量送到每一个它覆盖的点上,网络流的特性让这点很难实现。考虑换一种思路,把所有点从小到大遍历一遍,途中累加费用。这样的情境下,考虑选择一个区间,它让我们在遍历 \((l,r)\) 间点的过程中可以选择的区间个数减少了 \(1\),即,它以自身的费用为报酬,在我们的遍历过程中使我们暂时失去了一点区间选择权。
这和费用流十分相似,考虑以流量表示可以选择的区间个数。先构建出一条从小到大的主路:对于 \(i<N\),连边 \((i,i+1)\),容量 \(\inf\),无费用。对于 \((l,r)\),从 \(l\) 到 \(r\) 连一条容量 \(1\)、费用 \(r-l\) 的边:当流量在点 \(l\) 从主路上来到这条边时,主路上可供支配的流量便少了 \(1\) 点,即在 \((l,r)\) 中可以再选的区间个数少了 \(1\) 个;当流量在点 \(r\) 回到主路时,区间交还 选择这个区间花费的流量,不再影响后面的选择。这一过程中,流量收集了 \((l,r)\) 边上的费用。\(k\) 可重,所以我们从源头卡死总流量,源点向 \(1\) 连容量为 \(k\) 的边;在点 \(N\) 收集流量。求最大费用最大流即可。
22.最长k可重线段集问题
差不多是一模一样,但是可能会出现 \(x0=x1\) 的情况,与上一题不同。离散化,对于 \(x0=x1\),发现连边 \((x0,x1)\) 会直接出现正环从而爆炸。于是拆点,原来 \((l,r):l\ne r\) 的边从 \(l\) 的出点连向 \(r\) 的入点以便及时交还流量、选择其它线段;特别地,对于 \((l,r):l=r\) 的边从入点连向出点,“夹缝生存”,我们在入点刚收到的流量便可以直接派上用场,用完之后马上交还、不影响后面的选择。
23.餐巾计划问题
明显地用流量表示餐巾,在每一天间连边:源点向每一天提供 \(\inf\) 的流量,费用 \(p\);每一天向汇点连边,容量 \(r_i\);第 \(i\) 天向第 \((i+1)\) 天连边,容量 \(\inf\),表示餐巾可以存储到下一天,同时向 \(i+m\) 连容量 \(\inf\) 的边,费用 \(f\),慢洗部同理。发现这个方法会出问题,因为每天用完的餐巾要么流向汇点,要么通过快洗部、慢洗部的边流向后面的点,只能有一个去向,我们将每天连向汇点的目的就是让汇点接收餐巾的同时使餐巾向后面的点流去,所以这个局面是我们所不希望看到的。试着拆点,把每天拆成“新点”和“旧点”,每天使用过的餐巾先由新点流向旧点再作处理,发现无济于事。
由贪心可以知道脏餐巾要么扔掉不管,要么直接送到快洗部、慢洗部,没有存起来的必要。保留拆点的方法,我们试着改变每天使用餐巾的处理方式。第 \(i\) 天,我们需要 \(2\times r_i\) 份餐巾,一份流向汇点以统计最大流、保证每天餐巾的使用,另一份用来送到快洗部、慢洗部为以后作贡献。如果每天能有两个点来保证双份流量就好了……等一下,两个点?我们不是刚刚就把每个点拆开了吗?稍加思考,我们可以让两个点一个负责给汇点供流、一个负责为后面提供旧餐巾。明显,前者应该由新点完成,从新点向汇点连容量 \(r_i\) 的边;后者应该由旧点完成,它可以从源点处获取旧餐巾,从源点向旧点连容量 \(r_i\) 的边。最大流保证了新点连向汇点的边满流;对于源点连向旧点的边,我们不在乎它是不是满流,旧点从源点处拿到的餐巾一定是派上用场的,其它旧餐巾便是被扔掉不管的,它没必要拿。切断同一天新旧点之间的边,求最小费用最大流即可。
24.机器人路径规划问题
至今尚未解决,目前网络流最小复杂度 \(\Theta(n^9)\)。