最小割集Stoer-Wagner算法初探(crytal)

  • 格式:wps
  • 大小:71.00 KB
  • 文档页数:5

下载文档原格式

  / 5
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

最小割集Stoer-Wagner算法初探

首先抄一些网上关于该算法裸的解释:

1.min=MAXINT,固定一个顶点P

2.从点P用“类似”prim的s算法扩展出“最大生成树”,记录最后扩展的顶点和最后扩展的边

3.计算最后扩展到的顶点的切割值(即与此顶点相连的所有边权和),若比min小更新min

4.合并最后扩展的那条边的两个端点为一个顶点(当然他们的边也要合并,这个好理解吧?)

5.转到2,合并N-1次后结束

6.min即为所求,输出min

第一眼看到这么"精辟"的解释感到很蛋疼,画了很多图研究了好一会儿,突然发现解释地有点可爱。当然我并不是藐视这种解释,或许换个人看看会感觉解释地很自然,很清晰。

当然下面我就要解释一下它的可爱之处,和我对于该算法的理解。(这里我将采用混着论述的方法)

如果你对该算法要干什么不是很清楚请先看一下poj的2914 Minimum Cut ,题目连接:/problem?id=2914

目的:将图划分成S 和T两部分

算法主线:每次搜索完后便找到了当前的最小割,这时更新最小割的值,同时将该点扔进集合。

Step1: 算法开始,随便找一点P,然后从P开始累加和P点相连的边的连通度,并将它存储在wage[i]数组中,算法演示:

这里的wage初始值都为0

Step2: 这一步是keypoint ,而且也是我觉得网上的解释比较可爱的地方,在step1之后,就得到了相应的wage值,如果我们找到当前wage值最大的那个点(如果有多个这样的点那么任选其一),那么这时候这个点也就是在已选了P点之后能获得的最大的连通度的点,按照这个思路我们依次进行这个步骤: 在未访问过的点中找wage值最大的点,那么当算法进行到结束的时候,这时我们获得了这样的两个点S 和T ,其中T是最后找到的那个点,S是T刚刚前一个找到的点。那么这时候就可以得到一条十分有用的性质:

wage[T] = 将T这个点分离这幅图的连通度之和,也就是说sum(T的连通度)

这条结论很容易证明:因为每次找到一个点u的时候都对和这个点相连的点v加上了一个从u到v的连通度g[u][v](表示u,v之间点的连通度,即u,v有几条边连接),那么当当前的节点为T的时候,和T相连的所有的边的连通度必然都已经累加并存储在了wage[T]中。这时我可以不假思索地说wage[T]为S和T中较小的点的割集,要理解这一点可以看下面的两种情况(且只会出现这两种情况):

Condition 1 :

Condition 2 :

你可以自己先模拟一下上文提到过的步骤。

这里必须明确一点,那就在step2 中我们并不关心选出来的wage[T]是否是全局的最小割,

我们只关心wage[T]是S或者T中的最小割。以上面的两幅图来说就是,对于condition 1 由于S的选取早于T的选取也就是说在选择S或者T作为扩展的节点时,由于wage[S] >= wage[T] ,我们必须选S,接下来,如果当前节点是S那么无论是wage[S]还是wage[T],都只剩下(2,3)这条边的连通度没有更新。那么到底是应该更新S还是T,很明显我们应该更新T,因为在更新(2,3)边之前wage[S] >= wage[T] , 如果我们更新wage[T]那么获得的割集肯定是两者中较小的。Condition 2 的情况分析于此类似(这里我就不再赘述了)。这里提及一下,前面说我感到网上的算法描述很可爱,主要是首先step2和最大生成树更本一点关系也没有,只是模仿了最大生成树构造方法而已(形似而神不似)。

Step3:由于step2我们已经获得了S和T中的最小割,并且更新了最小割的值那么不妨将T扔到集合in_set 中,表示对T求割值已经ok了。接下来提出一个绝妙的想法(当然不是我自己想出来的),如果将S和T合并成一个点,并且更新S的相应元素的值,那么T便可以丢弃了。如果这条想法可行的话那么点的规模便会减小。只要扫描n - 1次便可以得到最终的解。下面证明一下其可行性。再次回到上面的两幅图,对于condition 1 ,只需将T移向S和S重合,那么原来和T相连的边,现在和S也相连了,这时候T和S在同一个连通块中,那么及时删去T也不会对后面的操作产生影响,原因:既然wage[T]表示S和T中的最小割,那么最小割必然不会是wage[S] + (S->T的连通度) , 而且我已经更新了最小割的值,那么现在即使将S和T合并也不会影响全局最小割的求解。而对于condition 2 的解释更加简单。Condition 2 中当当前扫描到的节点是T,那么wage[S] , wage[T]中已经保存了S和T 两个单节点的累计连通度之和。所以当将S和T合并之时,已经将T独立了,而S和T中的最小割又不是wage[T] 那么,将S和T合并,并不会影响后面的计算。

好了就三步可以得出最小割了,时间复杂度为O(n^3) 或者O(n^2logn).

总结:keypoint: 处理的时候始终是对单点求最小割,多点的最小割通过合并可以转化成单点的最小割。

Code (Stoer-WagnerO(n^3)) :

typedef long long Type;

bool in_set[cy] , vis[cy];

Type wage[cy];

Type Search ( int n , int &S , int &T ){

memset ( vis , false , n * sizeof ( vis[0] ) );

memset ( wage , 0 , n * sizeof ( wage[0] ) );

S = T = -1;

Type min_cut ;

int u = 0;

for ( int i = 0 ; i < n ; i++ ){

Type mx = -INF;

for ( int j = 0 ; j < n ; j++ ){

if ( !in_set[j] && !vis[j] && mx < wage[j] ){

mx = wage[u = j];

}