zoukankan      html  css  js  c++  java
  • 费用流

    (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) 列:

    1. (S)(A_i)([lim,0]) 的边,行限制
    2. (B_i)(T)([lim,0]) 的边,列限制
    3. 必选格 ((i,j)),从 (A_i)(B_j)([1,-100001]) 的边
    4. 可选格 ((i,j)),从 (A_i)(B_j)([1,-1]) 的边
    5. (A_i)(B_i)([inf,0]) 的边,强行保证行列相同时达到满流。

    从这道题我们也可以发现,直接应用费用流可以省掉许多上下界(当然也有不能省的),即通过把必选的边费用设成极值。

    回路限制

    相信大家一定牢记昨天lyc的讲课内容。

    回路,关键即有向图是入度=出度=1,无向图是度数=2。

    进入正题(好像比前面的直接应用容易一点

    占空间专用

    循环格

    传送门

    我心中的全局最简单题。

    他是个有向图回路,只要满足入度=出度=1,每个格子上有个箭头,已经保证了出度,只要保证每个格子恰好被某个箭头指到就好了。

    1. 对每个格子中的箭头建点,指向他所相邻的格子,若方向和原来不同则费用为1,否则不需要费用
    2. 源点到箭头连 ([1,0]) 的边,表示一个箭头选一个方向
    3. 格子向汇点连1,表示每个格子只能被指一次。

    最小费用最大流

    【TopCoder SRM570 900】CurvyonRails

    首先回路限制,这是个无向图,所以每个点度数为2。

    前一题中有我们是对箭头建点,那这题的边怎么办呢。我们遇到了和开头同样的问题,网络流是有向的,无向边怎么表示。按照前面的套路,我们知道要把他分成两堆,保证只有两堆之间可以连边。

    网格图常用套路:黑白染色。这样就只有黑点和白点直接可以连边了,黑点向相邻的白点连边。回路的限制很简单吧,源点到格点,格点和汇点都连 ([2,0]) 的边,一定要满流就好。

    弯路和直路怎么办呢?

    对于每个点,当且仅当它的两条边被分配到了都一行或同一列,它是直道。

    这启发我们把一个点拆成行点和列点,如果其中一个点被流了两次,那就要付出费用。

    好像还是不好办。

    我们强制让行点和列点都只连一个,然后行和列之间再连一条 ([1,1]) 的双向边(两条单向变),流过这条边就说明一个点流了了两次。

    简单来说,先黑白染色,对于某一个黑点 (x),将他拆成行点 (A_x) 和列点 (B_x)

    1. (S)(A_x,B_x)([1,0]) 的边
    2. (A_x,B_x) 分别向同行、同列的白点连 ([1,0]) 的边
    3. 白点向 (T)([2,0]) 的边。
    4. (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) 个人都等着,那么等待时间之和就是:

    [t_{a_1,j}w+t_{a_2,j}(w-1)+cdots+t_{a_w,j} ]

    那么对于同一道菜,它做得越靠后,费用就越大。

    套用费用递增模型

    球队收益

    传送门

    剪刀石头布

    传送门

    签到问题

    课件写的是签到问题,感觉还蛮形象的。

    大概就是必须到某一个点必须带着 (x) 个东西签一次到,那么就可以把这个点理解为早上和晚上,早上签到,晚上离开。

    早上你要上交 (x) 个物品,暂存在汇点(假装汇点是个大boss),即早点向汇点连 ([x,0]) 的边

    晚上你可以从源点重新拿回这 (x) 个物品(汇点下班了把东西转交给源点了),继续你的下一次签到。即源点向晚点连 ([x,0]) 的边。

    合法方案一定是满流的。

    遮题解

    餐巾计划

    传送门

    我们可以理解为,第 (i) 天早上,餐厅要发给顾客 (x_i) 块餐巾,可以买餐巾,洗衣店会把前几天送来的今天洗完的餐巾送回来。每天晚上顾客把所有餐巾都还回来。每天晚上餐厅可以把餐巾送到快(慢)洗点洗,当然也可以堆着不洗,直接放到明天(但是他不能发给顾客,所以等于屯了一天直接屯到第二天晚上)。

    把每一天拆成早点 (A_i) 和晚点 (B_i)

    早上能干吗要干嘛?

    1. 发餐巾:从 (A_i)(T)([x_i,0]) 的边,必须要满流
    2. 买餐巾:从 (S)(A_i)([inf,p]) 的边
    3. 收洗过的餐巾:见下

    晚上能干吗?

    1. 回收脏餐巾:从 (S)(B_i)([x_i,0]) 的边(可以理解会汇点和源点间有无形的边汇点收到的会回流)
    2. 洗餐巾:快洗—— (B_i)(A_{i+a})([inf,b]) 的边;慢洗——(B_i)(A_{i+b})([inf,a]) 的边。
    3. 直接留着扔给明天:(B_i)(B_{i+1})([inf,0]) 的边。

    跑最小费用最大流,一定是满流。

  • 相关阅读:
    python之面向对象
    Python常用模块(logging&re&时间&random&os&sys&shutil&序列化&configparser&&hashlib)
    Python之模块与包
    2.1 、寻找元素 (重要的选择器和筛选器)
    4、循环语句 和 异常处理
    7、其他知识点
    2、函数 面向对象
    3、数据类型
    1、初识JavaScript
    2、css
  • 原文地址:https://www.cnblogs.com/flyfeather6/p/13508650.html
Copyright © 2011-2022 走看看