最小割(uva1515-水塘)
输入一个h行w列的字符矩阵,草地用“#”表示,洞用“.”表示。你可以把草改成洞,每格花费d,也可以把洞填上草,每格花费f。最后还需要在草和洞之间修围栏,每条边花费b。整个矩阵的第一行/列,最后一行/列必须都是草。求最小花费。2<=w,h<=50 1<=d,f,b<=10000
想要理解这道题的解法,首先需要建立正确的“割”的概念。
事实上,这道题纠正了我对“割”的理解……
关于割的讨论
以下摘自算法导论
流网络G=(V,E)中的一个切割(S,T)将结点集合V划分为S和T=V-S两个集合,使得s∈S,t∈T。
切割(S,T)的容量是:
算法导论中定义了s-t割,即流网络中的源点s要在S集中,汇点t要在T集中。我们给出一个流网络,除了源点s和汇点t,还有a、b、c三个中间结点。
算法导论中是从点的角度定义割的,而我们应用时一般从边的角度考虑,即用割断的边的集合表示一个割。
那么问题来了,如下图所示,我们割断了边s→a、b→c,使得s和t不连通,那么边集{s→a,b→c}是这张网络图的割吗?
它并不是一个割,如果我们定义割集是组成割的容量的边的集合的话。由我们对割的一般认知,去掉割中的所有边,则S、T集不连通(确切地说,S无法连向T,而T可以连向S,这是割的容量的定义所内含的),所以对于这张图,显然S={s、a、b},T={c、t},如此s→a这条边就是S集内部的,不是横跨S、T的边,不是割的流量的组成部分。
去掉它,正确的割如下图:
正确的割1或者在割中新加入一条边b→a:
正确的割2这个割有一点奇怪,虽然我们把所有点划分成了S集{s、b},T集{a、c、t},并且割断且仅割断了所有满足"u∈S,v∈T"的边u→v,但是点a与汇点t却不连通!
事实上,割的定义中从来就没有说过,T集中的点都能流向汇点t,或者源点s能流向S集中的所有点。
换个角度来理解,每当你选择一条有向边u→v割断时,u及所有流向u的点将被自动地划入S集,v及v流向的所有点被自动地划入进T集。如此切割若干条边后使得最终的图逻辑自洽,即没有哪个点既属于S也属于T(在第一幅图中,a就是一个逻辑矛盾的点),这样的一个切割边集,才是一个正确的割。
用最大流算法跑出的最小割当然也是一个正确的割,而且是所有正确的割中容量最小的那一个。
回到题目上来
题目可以这样理解:每个格子是一个结点,整张图是一张有向图(流网络),每个格子拥有指向它上下左右4个邻居的4条有向边。我们需要把所有的格子划分成两个阵营:“草”或“洞”,并且选择一些边建立围栏(割断),使得“草”中的任何一点不能流向“洞”中任何一点,反之亦然。
这里有一点需要注意,我们建模时两个结点之间有一来一回两条边,所以建围栏时相当于这两条边同时割断了,因此我们不妨将目标修改为:割断一些有向边,使得“草”中任一点不流向“洞”中任何点,而不必关心“洞”中的点能否流向“草”中的点。这样做的极大好处在于:对于一对相邻点u、v,如果将u划分为草,v划分为洞,那么就割断u→v,反之就割断v→u。
为了建立最小割模型,引入源点s,将它连向所有初始的草点,并设这些边容量为d(草转洞的代价),引入汇点t,将所有初始的洞点连向它,容量设为f(洞转草的代价),并把之前建立的那些网格图上的有向边的容量设为b(建围栏的代价)。跑s-t最小割,所得最小割的容量即是最小总代价。
为什么呢?每当割断一条邻居间的边时,就做了一次决策(哪边划入草,哪边划入洞),每一组对于全局的决策都能够确定出一个割出来,不会遗漏情况。另一方面,由于s连向所有初始的草,如果它在决策中被划入洞,那么s连向它的边就一定会被切断,否则这个割是不自洽的,转换的代价也即是这条边的容量就因此被计算进去了。