1. 程式人生 > >無向圖全域性最小邊割集

無向圖全域性最小邊割集

一個無向連通網路,去掉一個邊集可以使其變成兩個連通分量則這個邊集就是割集;最小割集當然就權和最小的割集。

可以用最小切割最大流定理:

1.min=MAXINT,確定一個源點

2.列舉匯點

3.計算最大流,並確定當前源匯的最小割集,若比min小更新min

4.轉到2直到列舉完畢

5.min即為所求輸出min

    不難看出複雜度很高:列舉匯點要O(n),最短增廣路最大流演算法求最大流是O((n^2)m)複雜度,在複雜網路中O(m)=O(n^2),演算法總複雜度就是O(n^5);哪怕採用最高標號預進流演算法求最大流O((n^2)(m^0.5)),演算法總複雜度也要O(n^4)

    所以用網路流演算法求解最小割集複雜度不會低於O(n^4)。

---------

    prim演算法不僅僅可以求最小生成樹,也可以求“最大生成樹”。最小割集Stoer-Wagner演算法就是典型的應用例項。

    求解最小割集普遍採用Stoer-Wagner演算法,不提供此演算法證明和程式碼,只提供演算法思路:

1.min=MAXINT,固定一個頂點P

2.從點P用“類似”prim的s演算法擴展出“最大生成樹”,記錄最後擴充套件的頂點和最後擴充套件的邊

3.計算最後擴充套件到的頂點的切割值(即與此頂點相連的所有邊權和),若比min小更新min

4.合併最後擴充套件的那條邊的兩個端點為一個頂點(當然他們的邊也要合併,這個好理解吧?)

5.轉到2,合併N-1次後結束

6.min即為所求,輸出min

prim本身複雜度是O(n^2),合併n-1次,演算法複雜度即為O(n^3)

如果在prim中加堆優化,複雜度會降為O((n^2)logn)

這個Stoer-Wagner演算法可以參見這篇paper(http://docs.google.com/fileview?id=0BwxLvD9mcDNtMjk3MWVkMTAtZjMzNi00ZWE3LTkxYjQtYTQwNzcyZTk3Njk2&hl=en), 其核心思想是迭代縮小規模, 演算法基於這樣一個事實:

對於圖中任意兩點s和t, 它們要麼屬於最小割的兩個不同集中, 要麼屬於同一個集.

如果是後者, 那麼合併s和t後並不影響最小割. 基於這麼個思想, 如果每次能求出圖中某兩點之間的最小割, 然後更新答案後合併它們再繼續求最小割, 就得到最終答案了. 演算法步驟如下:

1. 設最小割cut=INF, 任選一個點s到集合A中, 定義W(A, p)為A中的所有點到A外一點p的權總和.

2. 對剛才選定的s, 更新W(A,p)(該值遞增).

3. 選出A外一點p, 且W(A,p)最大的作為新的s, 若A!=G(V), 則繼續2.

4. 把最後進入A的兩點記為s和t, 用W(A,t)更新cut.

5. 新建頂點u, 邊權w(u, v)=w(s, v)+w(t, v), 刪除頂點s和t, 以及與它們相連的邊.

6. 若|V|!=1則繼續1.

看起來很簡單, 每次像做最大生成樹一樣選最大"邊"(注意, 這裡其實不是邊, 而是已經累計的權值之和, 就當是加權的度好了), 然後把最後進入的兩個點縮到一塊就可以了. 合併點最多有n-1次, 而不加堆優化的prim是O(n^2)的, 所以最終複雜度O(n^3), 要是你有心情敲一大坨程式碼, 還可以在稀疏圖上用Fibonacci Heap優化一下, 不過網上轉了一圈, 大多都是說能用Fibonacci Heap優化到怎樣怎樣的複雜度, 真正能自己寫出來的恐怕也沒幾個, 看看uoregon(俄勒岡大學)的一大坨程式碼就有點寒. (http://resnet.uoregon.edu/~gurney_j/jmpc/fib.html)

特別注意幾個地方, 網上的好幾個Stoer-Wagner版本都存在一些小錯誤:

1. 演算法在做"最大生成樹"時更新的不是普通意義上的最大邊, 而是與之相連的邊的權值和, 當所有邊都是單位權值時就是累計度.

2. "最後進入A的兩點記為s和t", 網上對s有兩種解釋, 一是在t之前一個加進去的點, 二是t的前趨節點, 也就是最後選擇的那條邊的另一端. 正解是第一種!

3. 對於稠密圖, 比如這題, 我用堆, 對映二分堆, 或者STL的優先佇列都會TLE, 還不如老老實實O(n^3).




 1#include <iostream> 2#include <cmath> 3usingnamespace std;
 4int mat[600][600];
 5int res;
 6//Stoer-Wagner演算法,加了自己看得懂的備註
 7//無向圖全域性最小割,用求prim類似方法o(n^3),學習了一個下午……
 8//一開始用列舉源點匯點的最大流求解,複雜度o(n^5) 超時 9int min(int a,int b)
10{
11    if(a<b) return a;
12    elsereturn b;
13}
14
15void Mincut(int n) 
16{
17    int node[600], dist[600];
18    bool visit[600];
19    int i, prev, j, k;
20    for (i =0; i < n; i++)
21        node[i] = i;
22    while (n >1
23    {
24        int maxj =1;
25        for (i =1; i <n; i++//初始化到已圈集合的割大小26
27            dist[node[i]] = mat[node[0]][node[i]];
28            if (dist[node[i]] > dist[node[maxj]])
29                maxj = i;
30        }
31        prev =0;
32        memset(visit, falsesizeof (visit));
33        visit[node[0]] =true;
34        for (i =1; i < n; i++)
35    {
36                        if (i == n -1)//只剩最後一個沒加入集合的點,更新最小割37
38                                res = min(res, dist[node[maxj]]);
39                                for (k =0; k < n; k++//合併最後一個點以及推出它的集合中的點40{
41                    mat[node[k]][node[prev]]=(mat[node[prev]][node[k]]+=mat[node[k]][node[maxj]]);
42                }
43                                node[maxj] = node[--n]; //縮點後的圖44                        }
45                        visit[node[maxj]] =true;
46                        prev = maxj;
47                        maxj =-1;
48                        for (j =1; j < n; j++)
49            {
50                                if (!visit[node[j]]) //將上次求的maxj加入集合,合併與它相鄰的邊到割集51
52                                      dist[node[j]] += mat[node[prev]][node[j]];
53                                      if (maxj ==-1|| dist[node[maxj]] < dist[node[j]])
54                                      maxj = j;
55                                }
56            }
57        }
58
59    }
60    return;
61}
62
63int main()
64 {
65    int n, m, a, b, v;
66    while (scanf("%d%d"&n, &m) != EOF)
67    {
68        res = (1<<29);
69        memset(mat, 0sizeof (mat));
70        while (m--)
71    {
72            scanf("%d%d%d"&a, &b, &v);
73            mat[a][b] += v;
74            mat[b][a] += v;
75        }
76        Mincut(n);
77        printf("%d\n", res);
78    }
79    return0;
80}
81

相關推薦

全域性

一個無向連通網路,去掉一個邊集可以使其變成兩個連通分量則這個邊集就是割集;最小割集當然就權和最小的割集。 可以用最小切割最大流定理: 1.min=MAXINT,確定一個源點 2.列舉匯點 3.計算最大流,並確定當前源匯的最小割集,若比min小更新min 4.轉到2直到列舉完畢 5.min即為所求輸出min  

hdu 1599 find the mincost route 環 求從一個點遍歷所有節點以後回到原點的

                在寫題解之前給自己打一下廣告哈~。。抱歉了,希望大家多多支援我在CSDN的視訊課程,地址如下:http://edu.csdn.net/course/detail/209題目:find the mincost routeTime Limit: 1000/2000 MS (Java

hdu1599 【環】

杭州有N個景區,景區之間有一些雙向的路來連線,現在8600想找一條旅遊路線,這個路線從A點出發並且最後回到A點,假設經過的路線為V1,V2,....VK,V1,那麼必須滿足K>2,就是說至除了出發點以外至少要經過2個其他不同的景區,而且不能重複經過同一個景區。現在860

Beehives 找環..BFS..

                 題意:                            給了一個無向圖(至多700個點,兩點間無重邊),問其中邊數最少的環是所少條邊                  題解:                           這類問

SSL-1763 觀光旅遊(求環問題)

目錄 題面 Description   在桑給巴爾島的Adelton城鎮上有一個旅遊機構。它們決定在提供許多的其它吸引之外,再向客人們提供旅遊本鎮的服務。 為了從提供的吸引服務中儘可能地獲利,這個旅遊機構接收了一個精明決定:在相同的起點與終點之間找出

解法

from: http://www.cppblog.com/imky/archive/2010/08/14/123414.html 無向圖最小點割集,確定起點S,終點T。每個點都有自己的點權值vi,求最小點權和的割點集,使得S無法到達T。 解法:將每個點拆分為兩個點v和v',

HDU1845Jimmy’s Assignment(大匹配)

comment 最大匹配 tex dfs asc ddc repr freopen ces 題意:就是求最大匹配 #include<cstdio> #include<iostream> #include<algorit

HDU - 6311 Cover(的最少路徑覆蓋 歐拉路徑)

algorithm 奇數 detail cos log csdn puts true its 題意 給個無向圖,無重邊和自環,問最少需要多少路徑把邊覆蓋了。並輸出相應路徑 分析 首先聯通塊之間是獨立的,對於一個聯通塊內,最少路徑覆蓋就是 max(1,度數為奇數點的個

[Data Structure & Algrithom] 小生成樹

kruskal算法 如果 data spa amp imu 數據 結點 實現 最小生成樹(Minimum Spanning Tree) - 連接所有頂點的邊的權值之和最小的樹 Prim算法 基本思路 - 設 圖的頂點集合為V;其最小生成樹的頂點集合為U 將某個頂點放入U

鏢局運鏢---小生成樹

  假設有n個城市和m條道路,對應無向圖中的點和邊。每條路的過路費對應邊的權值。鏢局現在需要選擇一些道路進行疏通,以便邊距可以達到任意一個城鎮,要求花費的銀子越少越好。換句話說,鏢局的要求就是用最少的邊讓圖連通(任意兩點之間可以互相到達),將多餘的邊去掉。   很顯然,要想

短路徑 迪傑斯特拉(dijkstra)演算法實現

Dijkstra演算法說明  http://ibupu.link/?id=29namespace ConsoleApp14 { class Program { public static int M = -1; static

怎麼證明權重不相同的加權小生成樹是唯一的 (論)

設G是所有邊權均不相同的無向聯通圖。 證明一: 首先,易證圖G中權值最小的邊一定是最小生成樹中的邊。(否則最小生成樹加上權值最小的邊後構成一個環,去掉環中任意一條非此邊則形成了另一個權值更小的生

短路徑求解演算法之——Dijkstra演算法【轉】

在準備ACM比賽的過程中,研究了圖論中一些演算法。首先研究的便是最短路的問題。《離散數學》第四版(清華大學出版社)一書中講解的Dijkstra演算法是我首先研究的源材料。       如何求圖中V0到V5的最短路徑呢?         java實現的方式如下:   

用Dijkstra演算法求解短路徑

  Dijkstra演算法是典型的演算法。Dijkstra演算法是很有代表性的演算法。Dijkstra一般的表述通常有兩種方式,一種用永久和臨時標號方式,一種是用OPEN, CLOSE表的方式,這裡均採用永久和臨時標號的方式。注意該演算法要求圖中不存在負權邊。

迪傑斯特拉演算法處理短路徑的(dijkstra)Java實現(指定兩點,求短距離及路徑)

其實不是原創哈,我寫不出來。       如何求圖中V0到V5的最短路徑呢?         java實現的方式如下:         第一步,根據圖來建立權值矩陣:        int[][] W = {      {  0,   1,   4,  -1,  -

Tarjan縮環(求雙)/有縮環(求雙)/求點雙

邊雙與點雙 不嚴謹的定義, 邊雙=刪掉一條邊依然連通 點雙=刪掉一個點依然連通 無向圖Tarjan求邊雙 先說無向圖。無向圖就比有向圖簡單一些,因為只有返祖邊而沒有橫叉邊。 用棧來儲存已訪問的點。 如果已經訪問過了,就把它當返祖邊處理(low=min(low,

The Unique MST——判斷一個連通小生成樹是否是唯一的

Think: 1知識點:判斷一個連通無向圖的最小生成樹是否是唯一的+最小生成樹_Prim演算法+記錄路徑 2題意:給定一個連通無向圖,判斷這個連通無向圖的最小生成樹是否是唯一的 3錯誤反思: 4思路: 1>思路1:第一遍Prim演算法求出路徑最小

每日一省之————加權小生成樹演算法(Prim/Kruskal演算法)

1.帶權重的邊的資料結構 /** * 該類物件可以表示圖中的一條邊 * @author lhever 2017年2月19日 下午5:10:49 * @version v1.0 */ public class Edge implements Com

poj 1734 Floyd算求有

題意:旅遊公司要開發一條新的路線 , 要求這是一個總路程儘可能短的環 , 並且不能只含兩個城市 , 除開起點外 , 不能重複走之前走過的城市 , 輸出這條路線? Floyd演算法求最小環 程式碼: //用floyd演算法 , 求有向圖的最小環 #include #include #include #i

怎麽證明權重不相同的加權小生成樹是唯一的 (論)

少包 size tail ati color post 中一 pos 否則 轉自:https://blog.csdn.net/liangzhaoyang1/article/details/51602926 設G是所有邊權均不相同的無向聯通圖。 證