图论 最小割Toer-Wagner算法

参考来源: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 并入 s
inline 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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值