【模拟费用流·入门篇】由兔子和洞谈起的模拟费用流常规模型
\(Preface\)
早就听说模拟费用流这个算法了,它优秀的时间复杂度也一直令余颇为向往。
最近,总算找到一篇比较良心的博客,让余真正入门了这个算法。
基础模型(【BZOJ4977】[Lydsy1708月赛] 跳伞求生)
- 一条数轴上有一些兔子和一些洞,兔子的坐标为\(x_i\),洞的坐标为\(y_i\)。
- 每只兔子只能往左边走,所花代价等于行走距离。
- 每个洞有一个权值\(v_i\),表示进入这个洞所需的代价。
- 每只兔子不一定要进洞,每个洞最多进一只兔子。
- 求最大代价。
考虑按坐标同时枚举兔子和洞,由于代价的计算式为\(x_i-y_j+v_j\),\(x_i\)确定时,就是要找最大的\(-y_j+v_j\),这只要开个堆即可求出。
但是,如果现在采取了最优策略,不一定就是全局的最优策略。
因此,模拟费用流有一个十分核心的退流(后悔)操作,即若第\(i\)只兔子选择了第\(j\)个洞,就把\(-y_j+v_j\)从堆中弹出(每个洞最多进一只兔子),再把\(-x_i\)加入堆中。
考虑这个东西的意义是什么,如果之后第\(k\)只兔子选中了\(-x_i\),就说明决定放逐第\(i\)只兔子,并让第\(k\)只兔子取代第\(i\)只兔子进入它原本选择的洞。
或许你会感到疑惑,为什么这里直接放逐第\(i\)只兔子,说不定可它以选择之前某个洞使答案更大呢?
但仔细想想,每只兔子其实是没有区别的,如果第\(i\)只兔子能够选择之前的某个洞使答案更大,那么第\(k\)只兔子就不应该选中\(-x_i\),而是直接选中那个洞。
由此可见,第\(i\)只兔子已经完全失去了作用,这一算法的正确性也得以证明。
具体操作步骤总结:
- 将所有兔子和洞一起按坐标排序,然后按坐标从小到大枚举兔子和洞。
- 对于第\(i\)只兔子,取出当前堆顶\(top\),判断\(x_i+top\)是否大于\(0\):
- \(x_i+top>0\):更新答案,弹出堆顶,并将\(-x_i\)加入堆中,以便后续退流。
- \(x_i+top\le0\):说明不会使答案变优,不进行操作。
- 对于第\(j\)个洞,将\(-y_j+v_j\)加入堆中。
稍加修改
- 一条数轴上有一些兔子和一些洞,兔子的坐标为\(x_i\),洞的坐标为\(y_i\)。
- 每只兔子只能往左边走,所花代价等于行走距离。
- 每个洞有一个权值\(v_i\),表示进入这个洞所需的代价。
- 每只兔子一定要进洞,每个洞最多进一只兔子。
- 求最小代价。
由于每只兔子一定要进洞,那么只要在一开始就让所有兔子都和一个距离它\(INF\)的洞匹配。
这样一来,这个匹配无论如何都无法退流,也就实现了所有兔子都进洞。
具体操作步骤和上面大体相同,只是无论\(x_i+top\)是否大于\(0\)都必须更新答案,这里略去。
常规模型
- 一条数轴上有一些兔子和一些洞,兔子的坐标为\(x_i\),洞的坐标为\(y_i\)。
- 每只兔子可以往左右两边走,所花代价等于行走距离。
- 每个洞有一个权值\(v_i\),表示进入这个洞所需的代价。
- 每只兔子一定要进洞,每个洞最多进一只兔子。
- 求最小代价。
仅仅是这样一个简单的修改,便让整个问题复杂了许多。
接下来就要对于各种可能出现的情形分别考虑:(注意,以下的代价均指在堆中的值)
- 考虑一只兔子\(i\)如果选择了一个代价为\(V\)的洞,对答案贡献就是\(x_i+V\)。
- 如果之后一个洞\(j\)替换了这个洞,答案应该加上\(y_j+v_j-2x_i-V\)(注意,兔子坐标对答案的贡献从正变成了负,故要减去\(2x_i\))。也就是说,要加入一只代价为\(-2x_i-V\)的兔子。
- 这种情况下别的兔子不能替换这只兔子,否则这只兔子就没有洞了,违背了每只兔子一定要进洞的原则。
- 考虑一个洞\(j\)如果选择了一只代价为\(W\)的兔子,对答案贡献就是\(y_j+v_j+W\)。
- 如果之后一只兔子\(i\)替换了这只兔子,答案应该加上\(x_i-2y_j-W\)。也就是说,要加入一个代价为\(-2y_j-W\)的洞。(注意,这里不会因为原兔子被替换而违背每只兔子一定要进洞的原则,因为它在洞\(j\)选择它之前就有一个匹配的洞,洞\(j\)被抢走后它又会找回原先的洞,一切都回到洞\(j\)选择它之前的样子)
- 如果之后一个洞\(k\)替换了这个洞,答案应该加上\(y_k+v_k-y_j-v_j\)。也就是说,要加入一只代价为\(-y_j-v_j\)的兔子。
看起来很绕,不过其实只要知道,兔子已经不是单纯的兔子,洞已经不是单纯的洞,但兔子和洞之间依旧存在着匹配关系,就仍旧可以用堆来解决这个问题。
无论怎样,之所以问题会这么复杂,只是为了高效地完成退流操作而已。
\(Postscript\)
唔姆,模拟费用流的确是一个有趣的东西。
本文仅仅通过逐步深入的方式引出了模拟费用流的常规模型,之后余还会对建立于该模型之上的模拟费用流的两种进阶模型进一步分析。
不过,这篇博客就到此完结啦!撒花撒花~~