【最小割】POJ-2914 Minimum Cut

Minimum Cut
Time Limit: 10000MS Memory Limit: 65536K
Case Time Limit: 5000MS
Description

Given an undirected graph, in which two vertices can be connected by multiple edges, what is the size of the minimum cut of the graph? i.e. how many edges must be removed at least to disconnect the graph into two subgraphs?

Input

Input contains multiple test cases. Each test case starts with two integers N and M (2 ≤ N ≤ 500, 0 ≤ M ≤ N × (N − 1) ⁄ 2) in one line, where N is the number of vertices. Following are M lines, each line contains M integers A, B and C (0 ≤ A, B < N, A ≠ B, C > 0), meaning that there C edges connecting vertices A and B.

Output

There is only one line for each test case, which contains the size of the minimum cut of the graph. If the graph is disconnected, print 0.

Sample Input

3 3
0 1 1
1 2 1
2 0 1
4 3
0 1 1
1 2 1
2 3 1
8 14
0 1 1
0 2 1
0 3 1
1 2 1
1 3 1
2 3 1
4 5 1
4 6 1
4 7 1
5 6 1
5 7 1
6 7 1
4 0 1
7 3 1
Sample Output

2
1
2


題意:給出一個圖,點之間有若干條邊,問最少需要割斷多少條邊使得該圖變得不連通。
思路:明顯的最小割,點之間邊的數量相當於權值。一般來說使用最大流的方法複雜度會很高 因此使用Stoer-Wagner算法。
關於這個算法找到以下描述:

  1. min=MAXINT,固定一個頂點P
  2. 從點P用類似prim的算法擴展出“最大生成樹”,記錄最後擴展的頂點和最後擴展的邊
  3. 計算最後擴展到的頂點的切割值(即與此頂點相連的所有邊權和),若比min小更新min
  4. 合併最後擴展的那條邊的兩個端點爲一個頂點(當然他們的邊也要合併)
  5. 轉到2,合併N-1次後結束
  6. min即爲所求,輸出min

這份代碼可以在3000+ms過該題,我終於看懂了node數組的作用
正是爲了降低複雜度。
每次建立完一棵生成樹的最後一條邊的時候,都要判斷它是不是最小割,換句話說,更新最小割,然後將這條邊上的兩個點合併,之後更新整張圖,那麼刪除了這個點之後,如果將它“變成”最後一個點,那麼下次就不需要枚舉了。複雜度就是這樣降下來了。

node[k] = node[--now];

代碼如下:

/*
 * ID: j.sure.1
 * PROG:
 * LANG: C++
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <climits>
#include <iostream>
#define PB push_back
#define LL long long
using namespace std;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;
/****************************************/
const int N = 505;
int G[N][N];
int dis[N], node[N];
bool vis[N];
int n, m;

int min_cut(int now)
{
    int ret = INF;
    for(int i = 0; i < n; i++) node[i] = i;
    while(now > 1) {//重複建立n-1次最大生成樹
        int k, pre = 0;
        memset(vis, 0, sizeof(vis));
        memset(dis, 0, sizeof(dis));
        for(int i = 1; i < now; i++) {
            k = -1;
            for(int j = 1; j < now; j++) if(!vis[node[j]]) {
                dis[node[j]] += G[node[pre]][node[j]];
                if(k == -1 || dis[node[k]] < dis[node[j]]) {
                    k = j;
                }
            }//找出最遠的點k
            vis[node[k]] = true;//作標記
            if(i == now - 1) {//當找到生成樹的最後一條邊的時候,進行合併操作並更新圖
                ret = min(ret, dis[node[k]]);
                for(int j = 0; j < now; j++) {
                    G[node[pre]][node[j]] += G[node[j]][node[k]];
                    G[node[j]][node[pre]] = G[node[pre]][node[j]];
                }
                node[k] = node[--now];//利用馬甲將k移動到最後並刪除
            }
            pre = k;
        }
    }
    return ret;
}

int main()
{
#ifdef J_Sure
    freopen("000.in", "r", stdin);
    //freopen("999.out", "w", stdout);
#endif
    while(~scanf("%d%d", &n, &m)) {
        memset(G, 0, sizeof(G));
        int u, v, w;
        while(m--) {
            scanf("%d%d%d", &u, &v, &w);
            G[u][v] += w;
            G[v][u] += w;
        }
        int ans = min_cut(n);
        printf("%d\n", ans);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章