Loading

网络流练习

网络流练习

若是一个点经过的次数有限制,则需要拆点

拆点的妙用博大精深

注:下文中 \(s\) 指超级源点,\(t\) 指超级汇点

这里是最大流dinic的代码和费用流EK的代码

最大流

P1231 教辅的组成

题意

现在有 \(a,b,c\) 三种东西,分别有 \(n1,n2,n3\)

告诉你 \(m1\)\(a,b\) 的对应关系,\(m2\)\(a,c\) 的对应关系

只有 \(a_i\) 同时对应 \(b_j\)\(c_k\)\(a_i\) 才能和 \(b_j\)\(c_k\) 配成一套

问在不重复的条件下最多可以配成几套

思路

很基础的最大流

由于 \(a\) 对应 \(b\)\(c\) ,考虑建图时把所有 \(a\) 放中间 \(b,c\) 放两边

\(s\) 向所有 \(b\) 连一条容量为 \(1\) 的边,从所有 \(c\)\(t\) 连一条容量为 \(1\) 的边

从所有 \(b\) 向所有 \(a\) 连一条容量为 \(1\) 的边,从所有 \(a\) 向所有 \(c\) 连一条容量为 \(1\) 的边

跑最大流就行

但是我 \(Wrong\ Answer\)\(QAQ\)

这是因为 \(a\) 在中间,若是像下面一样会算两次:

so,我们把中间的所有 \(a\) 拆成两个点,加一条容量为 \(1\) 的边

最大流即为答案

P2472 蜥蜴

题意

在一个 \(r\)\(c\) 列的网格地图中有一些高度不同的石柱,第 \(i\)\(j\) 列的石柱高度为 \(h_{i,j}\)

一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。

每行每列中相邻石柱的距离为 \(1\),蜥蜴的跳跃距离是 \(d\),即蜥蜴可以跳到平面距离不超过 \(d\) 的任何一个石柱上。

石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减 \(1\)(如果仍然落在地图内部,则到达的石柱高度不变)。

如果该石柱原来高度为 \(1\),则蜥蜴离开后消失,以后其他蜥蜴不能落脚。

任何时刻不能有两只蜥蜴在同一个石柱上。

思路

数据范围很小,考虑网络流

因为每个石柱都有经过次数限制,所以要位置 \(i\) 拆成点 \(i\)\(i'\) ,且 \(i\)\(i'\) 之间连一条容量为 \(h_i\) 的边

\(i\) 表示跳到了石柱 \(i\)\(i'\) 表示从 \(i\) 点开始跳向别的石柱

若是能从一个石柱 \(i\) 跳到另一个石柱 \(j\) ,那么就从 \(i'\)\(j\) 连一条容量为 \(inf\) 的边

若是能从一个石柱 \(i\) 跳出边界,那么就从 \(i'\)\(t\) 连一条容量为 \(inf\) 的边

我们可以假设最开始每只蜥蜴都不在石柱上,而是在源点 \(s\)

然后从 \(s\) 向每个蜥蜴初始位置 \(i\) 连一条容量为 \(1\) 的边,表示一只蜥蜴

这样就可以巧妙地解决这个问题

最大流 \(maxflow\) 表示的是最多有多少只蜥蜴能跳走

记录蜥蜴总数 \(sum\),跑一遍网络流,最终结果为 \(sum-maxflow\)

P2766 最长不下降子序列问题

题意

给你一个正整数序列 \(a_{1\sim n}\),求:

  1. 最长不下降子序列的长度 \(S\)
  2. 计算从给定的序列中最多可取出多少个长度为 \(S\) 的不下降子序列。(每个元素只允许使用一次)
  3. 计算从给定的序列中最多可取出多少个长度为 \(S\) 的不下降子序列。(\(a_{2\sim n-1}\) 只允许使用一次,\(a_1,a_n\) 可以使用无数次)

思路

第一问是经典问题,我们可以得到递推式:

\[f_i=\max_{j=1,a_j\leq a_i}^{i-1} \left\{f_j\right\}+1; \]

第二问考虑网络流

首先每个元素被限制了只能使用一次,因此拆点,把 \(i\) 拆成 \(i\)\(i'\)

\(f_i=1\) 则从 \(s\)\(i\) 连一条容量为 \(1\) 的边,表示 \(i\) 可以作为不下降子序列的起点

\(f_i=S\) 则从 \(i'\)\(t\) 连一条容量为 \(1\) 的边,表示 \(i\) 可以作为不下降子序列的终点

\(a_j\leq a_i,j<i\)\(f_i=f_j+1\),则从 \(j\)\(i\) 连一条容量为 \(1\) 的边,表示可以拼出长度 \(+1\) 的不下降子序列

显然,这样建图后跑一边最大流即可

对于第三问,我们只需要在第二问的基础上把 \(s\rightarrow 1\)\(n'\rightarrow t\)\(1\rightarrow 1'\)\(n\rightarrow n'\) 的容量均改成 \(inf\) 即可

最小割

P4313 文理分科

题意

\(n\times m\) 个人,每个人要么选文科,要么选理科

\(i\)\(j\) 列的人选文科可以获得快乐值 \(art_{i,j}\),选理科可以获得快乐值 \(sci_{i,j}\)

若是一个人选的文科且上下左右的人都是文科,他可以获得额外快乐值 \(sameart_{i,j}\)

若是一个人选的理科且上下左右的人都是理科,他可以获得额外快乐值 \(samesci_{i,j}\)

求这些人的快乐值总和最大是多少

思路

\(A\)\(B\) ,典型的最小割

先不考虑额外快乐值

在集合 \(S\) 中表示选文科,在集合 \(T\) 中表示选理科

\(s\) 向点 \((i,j)\) 连容量为 \(art_{i,j}\) 的边,从点 \((i,j)\)\(t\) 连容量为 \(sci_{i,j}\) 的边

这样求最小割,割掉最少不要的快乐值,剩下的就是答案

考虑额外快乐值

我们新建一个节点 \(x\) 对应点 \((i,j)\) 的文科额外快乐值,从 \(s\) 向点 \(x\) 连容量为 \(sameart_{i,j}\) 的边

考虑上下左右及自己都必须选文科,因此从 \(x\)\((i,j)\)\((i-1,j)\)\((i+1,j)\)\((i,j-1)\)\((i,j+1)\) 都连一条容量为 \(inf\) 的边

显然这五条边是不会被割掉的

因此若是这五个点中有两个点不在一个集合内,就必须要断掉它们之间的联系,因为 \(x\) 把它们连在一起了

此时不能割容量为 \(inf\) 的边,就只能割容量为 \(sameart_{i,j}\) 的边

理科的同理,我们新建一个节点 \(y\) 对应点 \((i,j)\) 的理科额外快乐值,从 \(y\) 向点 \(t\) 连容量为 \(samesci_{i,j}\) 的边

\((i,j)\)\((i-1,j)\)\((i+1,j)\)\((i,j-1)\)\((i,j+1)\)\(x\) 都连一条容量为 \(inf\) 的边

根据最小割=最大流

跑一边 \(dinic\) 得到最大流 \(maxflow\),最终的答案是 \(all\_happiness-maxflow\)

P2762 太空飞行计划问题

题意

给定一张图,有左侧的点和右侧的点,左侧的点点权为正(对应试验),右侧的点点权为负(对应器材),如果选择了左侧的某个点就必须要选右边的一部分点。要求最大化点权和。

思路

若是左侧的点需要右侧的点,则连一条从左侧的点向右侧的点的有向边,这样问题就转换为:

给定一个有向无环图,点有点权,选择一个子图,满足子图上如果选择了一个点就必须选择它后继的所有点。最大化点权和。

这是一个经典最小割问题,叫做最大权闭合子图问题

建图操作如下:

\(s\) 向所有正权值的点连一条权值为点权的边,从所有负权值的店向 \(t\) 连一条权值为点权绝对值的边

保留原图中所有的边且容量为正无穷

则原图中最大点权和为原图中正点权权值和减最小割

\(S\) 包含所有要的点,\(T\) 包含所有不要的点

最小割割去的边必然与 \(s\)\(t\) 相邻,因为其他的边容量均为 \(inf\)

当我们选择要一个正权值点的时候,必然会将其放入集合 \(S\),这样就会割掉与其相连的负权值点与 \(t\) 的连边,这时候割去了需要减去的值

当我们选择不要一个正权值点的时候,必然会将其放入集合 \(T\),这样就会割掉其与 \(s\) 相连的边,这时候割去了需要减去的值

这样求得的割就是原图正点权和需要减去的部分,最小割就能减的最少,这样点权和最大

P3227 切糕

题意

你在切一个蛋糕,蛋糕每个点有一个非负不和谐值 \(v\),切蛋糕的规则如下:

首先将每一个竖列的坐标用 \((x,y)\) 表示,数列上的点就是 \((x,y,z)\)

每次在每一数列选择一个点

对于数列 \((x,y)\) 我们选的点高度记作 \(f(x,y)\),那选的点坐标即为 \((x,y,f(x,y))\)

定义:竖列 \((x,y)\) 的相邻竖列坐标可表示为 \((x-1,y)\) \((x+1,y)\) \((x,y-1)\) \((x,y+1)\)

选点坐标时有一个限制:每一个竖列选的坐标 \((x,y)\) 与其相邻的竖列 \((a,b)\) 坐标高度差不超过 \(D\)

\(|f(x,y)-f(a,b)|\le D\)

现在求 \(\min\sum v[x][y][f(x,y)]\)

思路

先去掉高度差不超过 \(D\) 的条件,那么这就变成了一个求最小割的题目

我们直接把每一个竖列的所有点依次相连,容量为 \(v\) ,且从 \(s\) 连一条到这列起点,容量为 \(inf\) 的边,从这列终点连一条到 \(t\),容量为 \(inf\) 的边

但是还有高度差不超过 \(D\) 的条件,所以考虑让建好的网络中即使割掉高度差超过 \(D\) 的地方也无影响

于是我们就只需要从 \((i,j,k)\)\((i\pm 1,j,k-D)\)\(i,j\pm 1,k-D\) 连一条容量为 \(inf\) 的边

会发现这样的话即使割掉所有不合法的边仍能从 \(s\) 走到 \(t\)

画图模拟一下就可以理解了

最后的结果就是最小割

P2805 植物大战僵尸

题意

给定一个有向图,点有点权,选择一个子图,满足子图上如果选择了一个点就必须选择它之前的所有点。最大化点权和。

这里的之前所有点指从这个点出发,沿着反向边能跑到的所有点

思路

这个题无非就是这个的变式,是一个最大权闭合子图问题

但是这道题需要除去环,所以用拓扑排序

若是拓扑排序的队列为空但某个点入度仍大于 \(0\),则此点在一个环内,应该除去

而且这道题边是反的,因此跑完拓扑排序后把边反过来,跑一边网络流就行

具体做法不再叙述

费用流

P1251 餐巾计划问题

题意

一个餐厅在相继的 \(N\) 天里,每天需用的餐巾数不尽相同。

假设第 \(i\) 天需要 \(r_i\)块餐巾( \(i=1\sim N\))。餐厅可以购买新的餐巾,每块餐巾的费用为 \(p\) 分;或者把旧餐巾送到快洗部,洗一块需 \(m\) 天,其费用为 \(f\) 分,或者送到慢洗部,洗一块需 \(n\) 天(\(n>m\)),其费用为 \(s\) 分(\(s<f\))。

每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。

试设计一个算法为餐厅合理地安排好 \(N\) 天中餐巾使用计划,使总的花费最小。

思路

每一天要处理两件事:准备干净餐巾以及处理脏餐巾

因此考虑把每个点拆成两个:早上和晚上,早上准备干净餐巾,晚上处理脏餐巾

建立一个源点 \(s\) 和一个汇点 \(t\)

  • \(s\) 向第 \(i\) 天早上连一条容量为 \(inf\),费用为 \(p\) 的边,表示每一天可以购买任意多餐巾
  • 从第 \(i\) 天早上向汇点连一条容量为 \(r_i\),费用为 \(0\) 的边,表示第 \(i\) 天早上需要准备 \(r_i\) 的餐巾
  • 从源点向第 \(i\) 天晚上连一条容量为 \(r_i\),费用为 \(0\) 的边,表示第 \(i\) 天需要处理的脏餐巾数量
  • 从第 \(i\) 天晚上向第 \(i+1\) 天晚上连一条容量为 \(inf\),费用为 \(0\) 的边,表示第 \(i\) 天的脏餐巾留到第 \(i+1\)
  • 从第 \(i\) 天晚上向第 \(i+m\) 天早上连一条容量为 \(inf\),费用为 \(f\) 的边,表示快洗第 \(i\) 天的脏餐巾
  • 从第 \(i\) 天晚上向第 \(i+n\) 天早上连一条容量为 \(inf\),费用为 \(s\) 的边,表示慢洗第 \(i\) 天的脏餐巾

这样就做完了

P2153 晨跑

题意

\(n\) 个点,\(m\) 条边,起点为 \(1\),终点为 \(n\),每个边都有边权

\(1\sim n\) 最长有几条的路线(满足每次经过的点都不相同),且在这个情况下最少需要跑的路程是多少

思路

这不是最小费用最大流吗?

把原路线容量设为 \(1\) ,费用设为边权就可以求解

但是要满足每次经过的点都不相同

考虑拆点

把点 \(i\) 拆成 \(i\)\(i+n\),从 \(i\)\(i+n\) 连一条容量为 \(1\) 费用为 \(0\) 的边

这样就行了

答案分别是最大流和最小费用

P2469 星际竞速

题意

给一张 DAG,边有边权

你最开始不在图内

你可以耗费 \(a_i\) 的代价直接到达 \(i\) 点,也可以消耗 \(edge_{x\rightarrow y}\) 的代价从 \(x\) 走到 \(y\)

求使所有点都被走到的最小代价

思路

我们可以换一种理解方式:

每次都不在图内,然后瞬移到某一点 \(x\) ,然后再走

考虑费用流,让你在最大流的时候把每个点都遍历一遍,然后同时求出最小代价

\(s\) 点开始表示出发,走到 \(t\) 点表示结束

因此我们可以把所有边容量设为 \(1\),这样无论如何最大流的结果都不会变

若是你想在 \(x\) 点结束,你必定是位移到 \(x\) 或从某处走到 \(x\)

考虑拆点

我们把 \(i\) 点 拆成 \(i\)\(i'\) ,然后从 \(s\)\(i'\) 连费用为 \(a_i\) 的边,\(i'\)\(t\) 连费用为 \(0\) 的边,\(s\)\(i\) 连费用为 \(0\) 的边

若存在边 \(edge(i,j)\) \(i<j\),则从 \(i\)\(j'\) 连费用为 \(edge(i,j)\) 的边

这样的话对于每一个点 \(i\) 对应的 \(i'\) 都会走向 \(t\) ,并且必定会由 \(s\) 或前面的点走到,可以更新最小答案

所以这种做法正确,最小费用即为答案

P4843 清理雪道

题意

从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。

你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。

由于每次飞行的耗费是固定的,你想知道如何用最少的飞行次数才能完成清理雪道的任务。

思路

我们把图中的每一条边 \(edge_i\) 拆成两条边:一条流量为 \(1\),费用为 \(1\);一条流量为 \(inf\),费用为 \(0\)

然后源点向每个入度为 \(0\) 的点(也就是链覆盖的起点)连边,流量为 \(inf\),费用为 \(0\)

每个出度为 \(0\) 的点(也就是链覆盖的终点)向汇点连边,流量为 \(inf\),费用为 \(0\)

然后跑最大费用最大流,增广的次数即为答案

因为每次增广都相当于覆盖一条链

若原图中的边 \(edge_i\) 没有走过,增广过程中,程序肯定会优先选择费用为 \(1\) 的边,表示清理它

否则这条边已经清理过了,就只路过不清理

最大费用最大流能够保证用最优的方式覆盖所有边

因此当所有费用为 \(1\) 的边都被经过之后,整个图也就覆盖完了,答案就是覆盖的次数

posted @ 2022-07-07 18:47  Into_qwq  阅读(37)  评论(0)    收藏  举报