网络流24题——魔术球问题
题意:
有 n 个柱子,现往上依次放入编号为 1, 2, 3, 4, … 的球,要求:
- 每次只能在某跟柱子的最上面放球
- 同一根柱子中,相邻两个球的和为完全平方数
问你最多能放多少个球,并输出方案。
这道题我第一次做网络流 24 的时候(大概是 17 年)就看到了,当时应该是看了题解之后仍然一知半解,之后又尝试过几次,都没做出来。结果 5 年后随便看了一眼,居然一下子就做出来了,属实有点以外。是我思考能力真的增强了呢,还是只是恰好灵稽一动呢,我不好说 233 。
这道题要求最大的球数,乍一看好像要 run 最大流,最大的流量就表示能放的球数。但是从这个思路出发的话会发现很难设计约束。
如果转念一想,尝试能不能用二分等手段把问题转化为可行性问题,再用网络流判定,问题就简单多了。
假设现在已经确定球数,要判断 m 个球是否可行,那么只需抓住关键性质:完全平方数的限制只和相邻的球有关。如果我们能对每一个球都找到一个邻居,并且每个球最多有两个邻居,那就必然能组成若干条链。
继续往下思考,就可以发现一个柱子本质就是一个链表,每个球都指向自己下面的球。柱子本身可以抽象成一个节点,任何人都可以都往上连,表示新开一个节点。除了柱子就只能连向值比自己小,而且和为完全平方数的点。
这样左边是 m 个点,右边是 m 个点 + n 个柱子点,左边往右边可以放的点连边,权值为 1 。s 到左边,右边到 t 都连权值为 1 的边,表示一个点只能用一次。如果跑满流就表示存在合法解,这其实也是个完美匹配问题。
接下来可以二分答案,然后判断答案合法性,但是其实这道题点数还是很多的(答案最大有 1500),边数也很恐怖,所以感觉二分会很慢。我用的方法是每次在残余网络的基础上额外左右加两个点和若干边,然后再执行 dinic ,如果流量增量为 1 就表示此答案合法,可以继续往上加。dinic 算法的原理应该是保证即使是在跑了一半的残余网络上,也可以修正出最大流的。但是之前的流量已经被统计过了,所以 dinic 的结果实际上是流量增量。
输出方案可以检查中间的边,然后就可以建出若干个链表,表示每一个柱子。
据说这题有贪心做法,证明在 loj 讨论区上,有兴趣的大神可以观摩一下。
代码:
1 |
|