图论 最小割Toer-Wagner算法

来源:互联网 发布:golang http basicauth 编辑:程序博客网 时间:2024/06/11 14:28

参考来源:http://blog.sina.com.cn/s/blog_700906660100v7vb.html


这里讨论无向联通图全局最小割

方法一  因为两点的最小割其实就是最大流,所以我们可以用dinic算法,枚举源点与汇点, 枚举时间复杂度O(n^2) , Dinic算法 时间复杂度O(n^2m),总的时间复杂度O(N^4M),显然当n很大的时候,需要的时间特别大,

方法二  Toer-Wagner算法,这个算法的正确性此处不证明,.核心的思想就是借鉴Prim算法,扩展最大生成树,记录最后加入的两个点

这里先定义两个操作,:

                 1.对于图GVE) , 对于V的一个子集A, i 属于 V-A ,  W[i] = 子集A中所有的点到 i 的直接距离之和(这里的直接距离,是指两点直接相连的距离,不借助其它点)

              2. Unite(s,t) 合并两个点,将 t 点合并到 s 点中, 所有到 s 的 点的距离 加上到t的, s到其它点的距离 加上t到其他点的距离 (这些只处理指直接相连的点的距离)


用一张图说明合并操作

                                                                            

                                                                            

此时合并5,6点

    算法流程如下

1.A代表点的集合,初始为空 , W[i] = 0;

2. n-1 次循环 

令集合A={a}aV中任意点

遍历所有的点,求出V-A中的w[i] ,   

最大的点 i加入集合A中

直到所有点都加入集合A后, 记录最后加入的为 t , 倒数第二个加入的为 s ,合并 s , t

3 . 最小割为每次循环的w[t] 中的最小值


下面详细说明求s,t的过程 ,Poj (pku) 2914 Minimum Cut的第三个case为例,图为

 



GVE

我们设法维护这样的一个w[],初始化为0

我们把V-A中的点中w[i]最大的点找出来加入A集合;

V-A直到为空

w[]的情况如下

w[i]

0

1

2

3

4

5

6

7

初始值

0

0

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

0

0

A=A{1}

 

---

2

2

1

0

0

0

A=A{2}

 

 

---

3

1

0

0

0

A=A{3}

 

 

 

---

1

0

0

1

A=A{4}

 

 

 

 

---

1

1

2

A=A{7}

 

 

 

 

 

2

2

---

A=A{5}

 

 

 

 

 

---

3

 

A=A{6}

 

 

 

 

 

 

---

 

 

上图一行一行的计算, 第三行, A中加入0点后, V-A 到A的w[i] , 如果有大小相同的,加入第一次出现的,即加入1点,

第四行, 加入1点后, 到A 的w[i] ,再从中取一个最大的,把点2加入A,

........


记录最后加入A的节点为t=6,倒数第二个加入A的为s=5,则s-t的最小割就为w[s],在图中体现出来的意思就是5-6的最小割为w[s]=3

然后我们合并 s ,t ,得到下图



G(V’,E’)

重复上述操作

 

 

 

w[i]

0

1

2

3

4

5

7

初始值

0

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

0

A=A{1}

 

---

2

2

1

0

0

A=A{2}

 

 

---

3

1

0

0

A=A{3}

 

 

 

---

1

0

1

A=A{4}

 

 

 

 

---

2

2

A=A{5}

 

 

 

 

 

---

4

A=A{7}

 

 

 

 

 

 

---

s=5t=7    s-t最小割是4

合并5 ,7 得到



 

 

w[i]

0

1

2

3

4

5

初始值

0

0

0

0

0

0

A=A{0}

---

1

1

1

1

0

A=A{1}

 

---

2

2

1

0

A=A{2}

 

 

---

3

1

0

A=A{3}

 

 

 

---

1

1

A=A{4}

 

 

 

 

---

4

A=A{5}

 

 

 

 

 

---

s=4t=5    s-t最小割是4

合并4 ,5 得到




 

 

w[i]

0

1

2

3

4

初始值

0

0

0

0

0

A=A{0}

---

1

1

1

1

A=A{1}

 

---

2

2

1

A=A{2}

 

 

---

3

1

A=A{3}

 

 

 

---

2

A=A{4}

 

 

 

 

---

s=3t=4    s-t最小割是2

一直循环N-1次,全局最小割即每次的W[t]中最小的


代码如下:


/* * 全局最小割Stoer-Wagner算法 * 时间复杂度O(n^3) , 求s,t的时候可以优化到nlogn , 总体 O(n^2logn),此处不优化 * 从0开始标号 * 无向联通图的最小割 */const int SIZE = 520;int graph[SIZE][SIZE];bool ISCombine[SIZE];bool ISvis[SIZE];int W[SIZE];int N,M;//合并两个点,将 t 并入 sinline void _unite( int s ,int t){    ISCombine[t] = true;    for (int i = 0;i < N;++i){        graph[s][i] += graph[t][i];        graph[i][s] += graph[i][t];    }}//寻找最大的W[i] , 并记录最后两个s,t下标int _SearchMaxWi( int &s, int &t ){    memset( ISvis , 0 , sizeof(ISvis) );    memset( W , 0 , sizeof(W) );    int tmpj = 1000;    for (int i = 0;i < N;++i ) {        int MAX = INT_MIN;        for (int j = 0;j < N ; ++j)            if ( !ISCombine[j] && !ISvis[j] && W[j] > MAX )                MAX = W[j] , tmpj = j;        if(t == tmpj) return W[t];        ISvis[tmpj] = true;        s = t , t = tmpj;        for (int j = 0;j < N;j++)            if ( !ISvis[j] && !ISCombine[j] )                W[j] += graph[t][j];    }    return W[t];}int Stoer_Wagner(){    memset(ISCombine , 0, sizeof(ISCombine));    int ans = INT_MAX;    int s,t;    for (int i = 0;i < N-1;++i ){        s = t = -1;        int tmp = _SearchMaxWi(s,t);        //cout <<s<<"   "<<t<<endl;        ans = min(ans, tmp); //用每次的W[i]更新ans        _unite(s,t); //合并s,t    }    return ans;}




0 0
原创粉丝点击