·为了有助于你读后文,在写题前先列出一些大米饼的代码习惯:
一个提醒:所有的ADD函数无特殊说明均如图:(没有w就直接跳过)
以及: go(i,a,b)=====for(int i=a;i<=b;i++),mem(a,b)==memset(a,b,sizeof(b))
并且:大米饼一般会使用ISAP算法求最大流,用Edmonds-Karp-SPFA算法求费用流。
[1]星际转移问题◤
【最大流问题】
·特 {MOD}:你不知道是在哪一天可以使全部人到达月球。
(误区:二分答案。不要这样做,因为最大流随时都处于最优解状态,所以我们只需要轻轻地枚举天数【T++】)
·建图思路:
(1)创建源点汇点,即地球和月球。
(2)飞船从地球接人,在月球放人,所以把在此位置的飞船与源点(汇点)建边。
(3)飞船昨天的状态,今天的飞船继承,这两个飞船之间建边。
(4)昨天空间站里面可能会有人,所以还要让昨天停靠在那里的飞船将他们运走(早运晚运,早晚都要运,这不影响最优决策)
SUM-UP:
在常规的建图上,这道题使得图富有动态感。这个叫做“分层图”,但为了方便理解,在这道题中我们可以看做是根据实际情况不断加边的图。
额外提醒:每条边的容量cap怎么规定呢?除了(4)中的容量为h[i]外,其余都可以设为INF(或许从地球出发时容量应限制为k,但是每一天都在判断,这一点变得无关紧要)。为什么?因为(4)操作代表了实际情况下飞船的运输情况,然而其他的操作实际上是逻辑讨论规定的(比如说(3)号情况,你总不可能让飞船上的人经过一天就卡死一部分吧?所以用INF是得当的)
另外,飞船是在移动的,可以将这种情况转化为给飞船当天停靠的空间站和昨天的空间站建边,所以飞船建边实际上就是将空间站节点建边。所以,这里容易产生的误区是:当前的图是表示当天状态的。不是,这图是表示至今为止的叠加状态,这样才满足最大流的性质。
·下面的第一幅图设置了一种非常简单的情况:
只有一个飞船,三个空间站(是ABC,不要看成了太空里总共有15个空间站啦)。所以不要认为橙 {MOD}状态继承没有意义,因为在有其他飞行周期和航道不同飞船的时候,这些边就有了意义。
很明显的是,这道题的建模部分代码长度远远超过了网络流算法的长度,这里只列出了建模过程(下面的题大都也会这样)。
n(太空站个数),m(太空船个数)和k(需要运送的地球上的人的个数)
上图中注释有一误:应将“来自昨天的飞船”改为“昨天空间站的人留到今天”,类似的将“昨天的空间站”改为“飞船将空间站的人运走”。
[2]负载平衡问题◤
【最大流最小费用问题】
·特 {MOD}:环状网络;只能向相邻的点转移货物;求出最小转移货物量。
·建图思路:
(1)流动的货物来自于仓库,它往何处去?回到另一仓库。所以仓库们既是源点也是汇点,所以必须处理一下:建立超级源汇点(S and T)。值得注意的是,连向汇点的容量均为average(平均值),这样美妙地满足了最终需要每个仓库货物相同的需要。
(2)考虑向两侧相邻仓库运输的情况,进行拆点操作(这样建出来的图比较清晰,可以避免重边),一个点被拆成“前点”和“后点”,那么就B仓库的点来讲,就从B后点引出两条各自通向A前点和C前点的边(不是双向的,因为如果要反方向运,可以从C或A后点连边向B前点吗,双向边是多余的)
如图:所有边的容量cap均为INF(因为题目中没有对它的限制)。然而我们的目的是:尽可能少走蓝 {MOD}的边(因为这表示仓库之间货物的转移,它的多少就是答案呀!)因此给这些“该死”的边加上相同的费用(其他边是无辜的,它们的费用均为零)
(注意,图中只绘制出了向一侧的相邻点的边,因为再画就混乱不直观了)
·以下是建模部分代码:
(一个处理技巧:在拆点的时候,设原来的点为i,则拆成的两个点的编号为:i和i+n)
[3]深海机器人问题◤
【最大流最小费用问题】
·特 {MOD}:很典型的网络流棋盘建模问题,需要考虑位置关系。
·建图思路:
(1)每个点可以随便走,但是它上面的美妙宝藏只能捡拾一次。转化为最大流问题就是:不论走多少次,也只能在这个点上获得一次美妙宝藏。所以我们想到了一条边,一条容量为1(只能捡1次),权值为宝藏价值的边。但是对于其他来到这里的机器,他们依旧可以借道过去,只是没有宝藏罢了,所以再加上一条容量inf,没有权值的边,毫无疑问,需要拆点,用以上两种边相连(TIP:点i可以拆成i和i+n,n是总点数,是不是很美妙?)。
(2)有k个探测器,那么就在建立超级源点汇点时,那条边容量为k就可以。
·这种方法很好理解,下面还有一种直接建边的方式,都没什么问题!
[4]最长k可重区间集问题◤
【最大流最小费用问题】
·特 {MOD}:覆盖关系和线段长度两个元素的转化
·建图思路:(在这里我们使用左闭右开区间)
(1)每个点最多被k个线段覆盖。那么可以转化为某些边的容量为k。你有你的权利放弃某条线段,所以对于每个点,与它后面的一个点建边,容量为k,没有权值。同时对于区间[L,R),向节点L,R加上一条边,一条容量是1的边。
(2)最终答案要求所选择的线段长度总和最大。这可以转化为一个权值问题,所以就把(1)中边的权值设置为该线段的长度。
【注意:不是每道题都很温柔,你可能需要离散化】
Sadly,codevs上面这道题的数据有错误。
但是这个题型很经典,可以变式,可以与其他问题结合。
[5]杂货商供应问题◤
【最大流最小费用问题】
·英文题,述大意:
N个供应商,M个店主,K种物品。输入每个供应商对每种物品的的供应量、每个店主对每种物品的需求量、从不同的供应商运送不同的货物到不同的店主手上不同的花费,以及从供应商Mj送第kind种货物的单位数量到店主Ni手上所需的单位花费。供应是否满足需求。满足时求出最小运费。
·网上一位优秀博主给出了输入样例分析:【CLICK】
·特 {MOD}:关系清晰,商品相对独立,建图开销不大。
·建图思路:
(1)杂货商(供应)有一个供应量,那就用一个超级源点来建边指向每一个杂货商,边的容量即为杂货商的供应量。
(2)店主(购买)有一个需求量,那么就将杂货商和店主们连上边,容量为杂货店的需求量(这里不要错写成店主需求,不然这就表明商品运到半途突然卡死了一部分……),权值为运费(别忘了反向弧时是相反权值)。类似的,来一个超级汇点,与各个店主相连,容量为店主需求量。
(3)一个漏掉的问题:有多件商品。那么就枚举,对于每件商品,按照上述方式建图即可。
如下是建模部分的代码:
[6]小人回家问题◤
【最大流最小费用问题】
·英文题,述大意:
读入n,m,和一个n*m的矩阵,矩阵中H代表房子,m代表一个小人人,其余是空地。每个小人人要寻得一个家,到达家中的话费是两点之间的马哈顿距离,输出最优分配的情况下的最小费用。
·特 {MOD}:典型,就是无比典型。
·建图思路:
(1)一人一个房子,所以用超级源点和每个人建边,边容量为1,没有权值。
(2)同理,每个房子和超级汇点也这样建边。
(3)每个人要前往房子。将每个人和每个房子纷纷建边,权值即为曼哈顿距离,容量为1。
以下是建模部分代码:
[7]双核CPU问题◤
【最大流问题】
·英文题,述大意:
读入n,m,表示有n个任务和m个限制。每个任务只能交给给定的两个处理器中的一个处理。接下来n行输入a,b表示第i个任务在CPU_A上和CPU_B上处理的花费。接下来m行输入x,y,z,表示如果x任务和y任务不被同一个处理去处理,那就要额外花费z的价值。求如何分配使得总花费最小。
·特 {MOD}:两个处理器!
·建图思路:
(1)每个任务在不同处理器上的花费不同,那就以CPU_A,CPU_B为超级源点汇点,分别于每个处理器建立单向边(注意方向)。
(2)考虑给定的限制条件。那么就将x,y之间建立一条双向边,容量为z。
·So,这道题求什么呢?最大流,还是最小割的容量?
这个图建出来怎样理解?
·考虑这是一个取舍问题(二分图模型),在建的图中,什么才叫做选择了把某个任务交给某个CPU去完成?
从简单情况入手:没有m个限制,如图:
·进行一遍最大流算法后,图中红边满载。我们发现最大流算法的美妙之处是可以自动取MIN(a,b)。而在这种情况中,满载边正好表示你为任务选择了处理器,从而得到最优解。
·为了加速理解,现在取一个极端:题目有一个限制,那就是任务1和任务2如果不在统一处理器,你将额外支付100000000000000。
你感到绝望,所以你决定把1,2号任务全都放到CPU_A中去完成。如果现在进行一遍最大流算法,如图:
·图中红 {MOD}的边依旧是满载边,黄 {MOD}边表示可能满载(这取决于你的建边以及枚举顺序),我们可以得到一个小小结论:必定满载的边就是最优的选择。因为流量在此无法扩大(这里也就是最小割的位置)。再深入一点,从实际意义来讲,如果你保持先前的选择(任务1,2不在同一个处理器),那么必须保证那条因为限制而加的“大边”一定流满(表示你愿意支付所有额外花费)。相反地,如果限制的花费不是很大的话,你可能还会坚持原来的选择,这时将上图的颜 {MOD}反过来就是了。所以本题求最大流。
·下面是建模部分的代码:
[8]来回最短路问题◤
【最大流最小费用问题】
·英文题,述大意:
一个图,起点终点固定,来回一次,不重复任何一条道路,求最小道路数(就是路径长度)
·特 {MOD}:在进行两次Dijkstra爆炸后许多人意识到这是一道网络流。
·建图思路:
(1)走两次。那么超级汇点超级源点与终点起点的连边的容量为2,,无权值。
(2)求最小路径。那么每段路径就是一条边,容量为1,权值为1。
下面是建模部分的代码:
[9]公平分配问题from Rujia Liu
【最大流问题】
·述大意:
读入n,m表示有n个任务,m个处理器,接下来读入n个二元组(x,y),表示第i个任务可以拿给处理器x或y来完成。问如何分配,是的处理任务最多的处理器所处理的任务数最少。
·特 {MOD}:二元组,“使最多的最少”。
·建图思路:
(1)每种任务只有一个,处理器可能会完成多个任务。所以超级源点连向所有任务,容量为1。所有处理器向超级汇点连边,容量为██████(小明写字时把钢笔墨水和大米饼洒上面了)。
(2)每个任务只会选择一种处理器。把任务和处理器建边,容量是1。
·你能求出被墨水遮盖的部分吗(每小题10分)。
·首先我们可以肯定的是,处理器连向超级汇点的边容量就是这个处理器处理的任务数。但我们什么都不知道。注意这道题目的问题,所以二分解决问题。即二分“任务数最多的处理器的任务数x”
(代码略,由于大米饼还没有FQ,所以LA 3231一题暂时进不去)
[10]网络扩容问题from Rujia Liu
【最大流问题】
·述大意:
(多组)读入n,e,c,表示n各节点,e条边(接下来会读入u,v,cap,表示每条有向边的数据),流量要求c。如果从1到n点之间存在大小为c的流,则输出possible,否则判断:是否可以改动一条边的容量,使得满足上述条件,如果可以改动,则将所有的可改动边输出(u从小到大,相同时v从小到大),否则输出not
possible。
·特 {MOD}:可以改动容量的网络流问题
·建图思路:(本来就是一张图啊)
·分析:
首先可以进行一次最大流算法,可以判断当前是否存在流满足c大小。
由于题目要求只能修改一条边,可以想到:如果要达到增流的目的,那么这条边一改,必定出现一条这条边所在的增广路,由最大流最小割定理可得这条边一定在该图的最小割中。
·所以我们可以这样玩:在第一次最大流结束后,把所有流量保存下来(具体方式是:把每一条边的cap减去当前的流量,再把流量清零,这样做方便后来的操作),然后求出最小割(这个根据最大流算法来定,比如这道题大米饼用的是ISAP/Dinic,那么只需要再进行一次BFS()刷新d数组,找到这样一条边(u,v): d[u])。随后枚举最小割中的边,将其容量改为c后进行一次最大流算法,如果新加的流与最初计算出来的flow的和大于等于c,则说明更改这条边是可以达到目的的(注意枚举每条边后的清零操作)。
·下面给出主函数代码:
[11]运输安排问题from Rujia Liu
【最大流问题】
·述大意:
输入n,m,k,s,t表示共有n个星球,m条双向路径,每条路径走过所需时间均为1天。现在需要将k个大米饼从起点S运往终点T,并且在同一时刻不能有两个(及以上)的大米饼同时在被运输(一车一饼制度,车辆单行制度)。求最少花费时间,并且输出每天的如下情况:发生移动的大米饼个数,以及输出(x,v)表示编号为x的大米饼在当天前往v星球。
·特 {MOD}:时间相关;求天数;输出具体情况
·建图思路:(与[1]类似思想)
(1)起点即源点,终点即汇点。
(2)将存在双向路径的星球连边。由于每天只能有一个大米饼通过,所以容量为1(结合下文理解便是了)。
(3)天数不定,用枚举天数代替二分答案。随着天数增加,不断添加新边新点。也可以理解为,将一个星球点拆成day个点。完善地说,将若a,b之间本来有路径,则将今天的b与昨天的a建立单向边,表示大米饼花费一天时间从a到达b,该边容量是1(就上(2)中的边升级版)。另外,这是一个和谐社会,所以人们要文明礼让,这不失为一个好计策。这启示我们大米饼可以在某个星球上停留几天,而不是随时走来走去呢?所以将昨天的a与今天的a连上一条边,容量inf。
·(图略,与[1]大同小异)
·如下为建模和打印部分代码:
·注意感叹号标记处,这里表示:可能会出现一条路上两个大米饼同时相向而行的情况(拆点的缘故,而且是双向边)。然而如果两个方向上都有流量,考虑到大米饼都是一样的,所以可以抵消。那么能够算是发生移动的大米饼就是代码中写的两种情况,这取决于流量的方向。
[12]收集者问题from Rujia Liu
·述大意:
鲍勃和他的朋友们都喜欢收藏不同种类的大米饼。鲍勃聪明,其他人笨。输入n,m表示包括鲍勃在内共有n个人,他们共有m种大米饼。接下来n行输入每个人的大米饼拥有情况(不一定每种大米饼每人都有,同种大米饼可重复输入;另外,第一行表示的是鲍勃的情况)。他们都可以,也只可以和鲍勃1对1地公平交换自己的大米饼,但由于智商原因,他们采取的策略不同:除鲍勃外的所有人只愿意用自己手里的重复种类的大米饼来向鲍勃换取他们没有的大米饼;鲍勃则很随意,只要公平交换就可以了。问鲍勃最大的可以收集的大米饼种类数。
·特 {MOD}:这是什么东西?
·建图思路:
(1)先讨论要求巨高的鲍勃的朋友们吧。梳理如下:
<1>自己有1个以上的这种大米饼,才会去交换。
<2>换回来的大米饼必须是自己以前没有的。
那么奇怪的是,这种关系放入最大流的图中,该怎样构建呢?好像是必须有东西一边进去,然后另一边给你吐出点其他的东西,这些新东西才能对答案有所贡献。所以鲍勃的朋友可以看作是一种“转换器”。
所以我们要构建一个图,可以表示:鲍勃给某个朋友一种大米饼(入边),然后这个朋友回报另一种大米饼(出边)。根据刚才的结论,对于一种大米饼,和同一个朋友,鲍勃最多拿这个大米饼去与这个朋友交换一次(以后这个朋友就有这种大米饼了,再找他交换,他会拒绝并狠狠地打你)。
形象化地,现在鲍勃拿A(饼)去换取朋友的B(饼),那么相当于一条单向边指向这个朋友,容量为1,一条单向边从朋友指出,容量也为1。如果我们将每种大米饼看做节点,那么A指向朋友依旧是容量为1的边,但是朋友的大米饼是给鲍勃的呀,而不是还给大米饼A的,所以指出的边的容量还是1吗?不一定,因为鲍勃可以拿另一种大米饼再来交换。所以出边的容量为:朋友拥有这种大米饼的数量减去1(表示他自己要留一个)。
(2)综合上述讨论,建图如下:
用m个节点表示不同的大米饼。从超级源点向m个大米饼连边,容量为鲍勃拥有该种类的数量。接下来考虑朋友们:
用n-1个节点表示每个朋友。对于每种大米饼和每一个朋友,如果这个朋友有这种大米饼(表示他可以提供)那么就用一条容量为该朋友拥有的个数减1边指向该种大米饼节点;如果这个朋友没有这种大米饼,那么就从该大米饼建一条容量为1(表示只有一次交换)边指向这个憨厚的朋友。
最后将每个大米饼指向超级汇点,容量为1(便于统计种类)。
·下面为建模部分的代码:
这道题的原题中真的有大米饼吗?
[13]生产销售规划问题from Rujia Liu
【最大流最小费用问题】
·述大意:
大米饼制造厂生产大米饼。输入M,I表示考虑M个月,存放一个月的代价为I。接下来m行描述每个月的情况:输入m,n,p,s,E,分别表示本月的单元生产成本,最大产量,售价,最大销售量和最多能够储存的月数。求怎样安排卖这个行为,使得获利最大。输出最大利润。
·特 {MOD}:时间相关。
·建图思路:
(1)有产量和销量。拆点,一个与超级源点相连,边容量为当月产量,另一个与超级汇点相连(表示卖这批商品),容量为当月销量。
(2)有多个月。继续拆点,给每一个月两个点(记为前点后点)。
(3)有保质期,所以将当月的前点与能够保存不变质的后来几个月的后点分别建边(表示几个月后卖出)。
注:程序中,记得加上存储费用带来的额外费用。
·给出建模部分代码:
[14]无源汇可行流问题▓
【无源汇可行流问题】
·英文题,述大意:
反应需要你降温,你不得行,所以要设计循环系统,保证一个循环流。读入n,m表示节点数和管子数。接下来n行读入u,v,Min,Max表示管子i是一个起点为u终点为v的单向边,容量下限是Min,容量上限是Max。问是否存在这样的循环系统(即每个点流量平衡,每支试管的流量满足上下界)。并输出每个边此时的流量。
·特 {MOD}:容量下限不为0,无源无汇。
·建图思路:
(1)这本来就是一个图啊,不用建。——小米饼
(2)上面一行在乱说胡说瞎说。目前连超级源点汇点都没有,怎么利用网络流知识解题?所以需要转化成已知问题。分析一个简单图片:
·先解决不熟悉的问题:容量下限怎么维持?如果我们将一条边的总容量(也就是容量上限)看成两部分:必须部分(从0到容量下限)和自由部分。以前做的题全都是只有自由部分,那是的生活是多么自由啊。
·所以我们的目标是保证红 {MOD}边永远满流,那么无论自由部分怎么玩,都可以满足容量上下界。怎么维护满流呢?这让我们联想到以前做过的一些要使用二分的网络流问题:我们判断二分结果是否满足条件的方法——设每个指向汇点的边容量为1(共m条),那么当最大流为m时,则满足二分的答案。所以为什么不把这些红边处理成连接汇点源点的边呢?用一条边来举例子:(在此我们建立一个美妙汇点和源点)
·所以,一个无源无汇的图可以这样改造:
·现在方法就很简单了:只需要保证SS的出边(或是TT所有入边,等价)满载,即里面的流量均达到容量限制(这里都指的是红 {MOD}的边:必须部分)。
所以肆意进行一次最大流(SS--->TT),如果最大流量等于SS所有的出边容量(即表示满载,TT入边也可以),那么说明存在这样循环流满足条件。
·下面是建模代码:
[15]有源汇最大流问题▓
【有源汇最大流问题】
·英文题,述大意:
一个你给m个大米饼拍照,计划拍照n天,每一天你给给定的C个大米饼拍照,每天拍照数不能超过D张,而且给每个大米饼i拍照有数量限制[Li,Ri],对于每个大米饼n天的拍照总和不能少于Gi,如果有解,求你最多能拍多少张照,并求每天给对应大米饼拍多少张照;否则输出-1。
·特 {MOD}:数量限制是区间(上下界),求最大数。
·建图思路:
(1)每天照片不超过D张。从超级汇点向每一天建边,容量为D。
(2)每天大米饼拍照数有一个区间限制,则将“每一天节点”向每一个大米饼连一条容量下限为Li,上限为Ri的边。
(3)对于每个大米饼,几天下来被拍的照片不得少于G张。从每个大米饼向汇点建立一条容量下限为G的边(上限inf)。
·所以就可以解决这道题了。但前提是你要会怎样求有上下界的最大流。依旧是问题转化,既然[14]题的思想那么好,我们应该继续传承。考虑怎样将这个问题转化为无源无汇之图以及怎样求出最大流。
·那么直接来说,分为两个步骤:保证基础和继续增广。
~保证基础:即保证所有边的流量满足的下界容量。用网络流原理可知,一切流从源点出发在汇点拜拜,那我们怎样去了解一路上的边是否流量都满足下界限制?那不如将从汇点向源点连一条只有容量上界且为inf的边吧,因为这样可以使整个图的流循环起来,既不会变少,也不会增加,而且使这个问题直接没有坏处地转化为无源无汇问题!在这个新图中用SS,TT按照相同的操作,便可以判断出这个图是否存在一种情况可以保证所有边的流量满足容量下限(如果不满足,求整个图最大流就别谈了,因为条件都不满足)。
~继续增广:注意在这之前的操作最多只能保证所有下限的满足(试回忆刚才图中的必须部分和自由部分)。所以现在需要将自由部分增广。很简单,直接从S到T(当然不包括那条inf边!注意:不是SS,TT——不要大米饼吃多了犯傻)进行一次最大流算法就可以了。这个问题的答案就是将两个步骤的流量加起来,YES!给出建模部分和主程序代码:
[15]有源汇最小流问题SGU 176
【有源汇最
小流问题】
·述大意:
n个节点,m条路径,接下来m行a,b,c,d,如果d等于1,则a到b的流量必须为c,如果d等于0,流量可以为0到c,问如果有可行流,输出最小流量和每条边的流量,否则输出impossible。
·特 {MOD}:最小流
·建图思路:
(1)本身就是一张图,关键在于如何解决这一类问题。下面给出网络上比较通用的“无源无汇最小流”的解法。
1.构造附加网络(不添加[t,s]边)
2.对ss、tt求最大流
3.添加[t,s]边
4.对ss、tt求最大流
5.若ss、tt满流,则[t,s]的流量就是最小流
Sadly,由于自己始终没能为大米饼推导出一个完整的证明体系,而网络资源匮乏,所以与其给大家分享网络上常用的“感性认识”,还不如保持沉默——懂就清楚地表达,不懂就自己学,不要用残缺无序的思想去误导他人。
如下是建模部分代码:
大米飘香的总结:
本文列出了一些基本题型,重在建模思路的探索和一些常用处理方式。但你也可以视作这是一个毫无头绪、纯属无聊而作的一页乱码(当然,大米饼不会这样认为),这样我会更加努力去完善。补充说明一个问题,在上下界网络流中,附加点SS,TT的建边方式是可以简化的,简化的结果也就是代码中的du[]数组,这种简化方式优化了程序,在应用时更加常用。还有就是,本文可能讹误众多,但毕竟是在用混乱的词句表达真挚的含义,希望读者收下大米饼的这份真诚!
————【Paul_Guderian】
2017.4.6 night
“也许下一次前行时依旧会倒下,可人生决不是向命运低头并祈求”————汪峰《不羁的生命》