(color{#FDF5E6}{简单})费用流
相信大家都听懂了前天pa讲的课和昨天lyc讲的课,所以今天就可以划水 了
对于一条边 ([flow,v]) 前者表示容量,后者表示费用。(当然有的时候他会变成表示上下界的自行辨别一下就好
废话连篇
放张图来当个封面遮一遮题解
直接应用
不经质疑(这真的是直接应用吗/泪奔
数字匹配
它是个费用流题,但是网络流都是单向的,这个匹配不是双向的吗
二分图匹配?这好像不是个二分图啊,来想想能不能把它分成两种,保证只有两种间能匹配,一种之内不能匹配。
匹配的条件是 (a_i=a_j*p) ,恰好多了一个质数因子,所以两者所含的质数因子总数奇偶行一定不同,奇数放左边,偶数放右边。
对于收益非负,每次找一条增广路,直到要负了为止。
Chips Challenge
好难啊
网格图、行、列,这提醒着我们建行点,列点,行列间连边,代表这个格子里放不放零件,不能就不用连边了,一定要放则 ([1,1]) 的边,随意则连 ([0,1]) 的边。
对于第二个条件,瞄一眼 (n),发现单行(列)可能的上限总共才只有 (40) 个,大胆地枚举上限,最后只要判断上限是不是 (le ans imes frac{A}{B}) 。
答案能二分吗?
经过多次偷数据得出:不行。比如这样的:
(A=2,B=3)
C.
.#
那么答案应该是 (3) ,但是当二分限制为 (1) 的时候,显然没有答案。
接下来就是烦人的第一个限制:行列相等。
如果他们相等,让它们同时加上一个流量,它们还是相等。因为他们的限制相等,所以如果让他们任意加上一个 ([0,inf]) 的流量,行和列可以同时满流,而如果原先不相等,加上这个流量之后,还是不相等。所以要求一个满足行列相等的,我们可以在同行同列间连一条 ([inf,0]) 的边,跑最大流看看满不满流。而行列间的边我们让其费用为 (-1) ,真实的零件数量即费用的绝对值。
所以现在就变成了枚举,建图,跑上下限费用流。但是真的不是很想跑上下限,怎么办呢?
怎么样才能让必选的边在最大流的情况下一定选进去?改费用!把必选的边权值付成 (-100001) ,最后看看费用绝对值除 (100000) 是不是刚好等于必须岸边条数。费用模 (100000) 就是答案。
好一个直接应用
简单地说,枚举每次的上限 (lim) ,设 (A_i) 表示第 (i) 行,(B_i) 表示第 (j) 列:
- 从 (S) 到 (A_i) 连 ([lim,0]) 的边,行限制
- 从 (B_i) 到 (T) 连 ([lim,0]) 的边,列限制
- 必选格 ((i,j)),从 (A_i) 向 (B_j) 连 ([1,-100001]) 的边
- 可选格 ((i,j)),从 (A_i) 向 (B_j) 连 ([1,-1]) 的边
- 从 (A_i) 到 (B_i) 连 ([inf,0]) 的边,强行保证行列相同时达到满流。
从这道题我们也可以发现,直接应用费用流可以省掉许多上下界(当然也有不能省的),即通过把必选的边费用设成极值。
回路限制
相信大家一定牢记昨天lyc的讲课内容。
回路,关键即有向图是入度=出度=1,无向图是度数=2。
进入正题(好像比前面的直接应用容易一点
占空间专用
循环格
我心中的全局最简单题。
他是个有向图回路,只要满足入度=出度=1,每个格子上有个箭头,已经保证了出度,只要保证每个格子恰好被某个箭头指到就好了。
- 对每个格子中的箭头建点,指向他所相邻的格子,若方向和原来不同则费用为1,否则不需要费用
- 源点到箭头连 ([1,0]) 的边,表示一个箭头选一个方向
- 格子向汇点连1,表示每个格子只能被指一次。
最小费用最大流
【TopCoder SRM570 900】CurvyonRails
首先回路限制,这是个无向图,所以每个点度数为2。
前一题中有我们是对箭头建点,那这题的边怎么办呢。我们遇到了和开头同样的问题,网络流是有向的,无向边怎么表示。按照前面的套路,我们知道要把他分成两堆,保证只有两堆之间可以连边。
网格图常用套路:黑白染色。这样就只有黑点和白点直接可以连边了,黑点向相邻的白点连边。回路的限制很简单吧,源点到格点,格点和汇点都连 ([2,0]) 的边,一定要满流就好。
弯路和直路怎么办呢?
对于每个点,当且仅当它的两条边被分配到了都一行或同一列,它是直道。
这启发我们把一个点拆成行点和列点,如果其中一个点被流了两次,那就要付出费用。
好像还是不好办。
我们强制让行点和列点都只连一个,然后行和列之间再连一条 ([1,1]) 的双向边(两条单向变),流过这条边就说明一个点流了了两次。
简单来说,先黑白染色,对于某一个黑点 (x),将他拆成行点 (A_x) 和列点 (B_x):
- (S) 到 (A_x,B_x) 连 ([1,0]) 的边
- (A_x,B_x) 分别向同行、同列的白点连 ([1,0]) 的边
- 白点向 (T) 连 ([2,0]) 的边。
- (A_x,B_x) 间连 ([1,1]) 的双向边。
最小费用最大流,满流才可行。
这题topcoder不太会用,所以有一道题大概可以替代
费用递增
费用递增,一个物品可以选择多次,而每次选择的代价递增。
可以对每一个物品 (A_i) 拆成 (A_{i,1} dots A_{i,x}) 每个的费用为选 (1 dots x) 次的费用,代表选某次。因为费用递增,不可能先选了后边的再选前面的,所以一定是前一条边流完了才有可能流下一条边。
本来要一次性把所有边都连完,那就完美的爆炸了,所以我们先只用每个点连一条边,等流过之后如果下一条不在图中就把他连上。
遮题解专用
美食节
考虑一个厨师贡献的代价
如果第 (j) 个厨师先后做了第 (a_1,a_2,cdots,a_w) 种菜,做倒数第 (k) 道菜的时候,后面的 (k) 个人都等着,那么等待时间之和就是:
那么对于同一道菜,它做得越靠后,费用就越大。
套用费用递增模型
球队收益
剪刀石头布
签到问题
课件写的是签到问题,感觉还蛮形象的。
大概就是必须到某一个点必须带着 (x) 个东西签一次到,那么就可以把这个点理解为早上和晚上,早上签到,晚上离开。
早上你要上交 (x) 个物品,暂存在汇点(假装汇点是个大boss),即早点向汇点连 ([x,0]) 的边
晚上你可以从源点重新拿回这 (x) 个物品(汇点下班了把东西转交给源点了),继续你的下一次签到。即源点向晚点连 ([x,0]) 的边。
合法方案一定是满流的。
遮题解
餐巾计划
我们可以理解为,第 (i) 天早上,餐厅要发给顾客 (x_i) 块餐巾,可以买餐巾,洗衣店会把前几天送来的今天洗完的餐巾送回来。每天晚上顾客把所有餐巾都还回来。每天晚上餐厅可以把餐巾送到快(慢)洗点洗,当然也可以堆着不洗,直接放到明天(但是他不能发给顾客,所以等于屯了一天直接屯到第二天晚上)。
把每一天拆成早点 (A_i) 和晚点 (B_i) 。
早上能干吗要干嘛?
- 发餐巾:从 (A_i) 到 (T) 连 ([x_i,0]) 的边,必须要满流
- 买餐巾:从 (S) 到 (A_i) 连 ([inf,p]) 的边
- 收洗过的餐巾:见下
晚上能干吗?
- 回收脏餐巾:从 (S) 到 (B_i) 连 ([x_i,0]) 的边(可以理解会汇点和源点间有无形的边汇点收到的会回流)
- 洗餐巾:快洗—— (B_i) 到 (A_{i+a}) 连 ([inf,b]) 的边;慢洗——(B_i) 到 (A_{i+b}) 连 ([inf,a]) 的边。
- 直接留着扔给明天:(B_i) 向 (B_{i+1}) 连 ([inf,0]) 的边。
跑最小费用最大流,一定是满流。