POJ 2914 Minimum Cut 无向图最小割

Minimum Cut
Time Limit: 10000MS   Memory Limit: 65536K
Total Submissions: 6869   Accepted: 2848
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 integersAB and C (0 ≤ AB < NA ≠ BC > 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
-------------

关于无向图的最小割(转)

http://wenku.baidu.com/view/fdb484c3bb4cf7ec4afed08b.html

目的:将图划分成S 和 T两部分 算法主线:每次搜索完后便找到了当前的最小割,这时更新最小割的值,同时将该点扔进集合。

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

Step2: 在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中较小的点的割集。

这里必须明确一点,那就在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]那么获得的割集肯定是两者中较小的。

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: 处理的时候始终是对单点求最小割,多点的最小割通过合并可以转化成单点的最小割。

-----------

/** head-file **/

#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <list>
#include <set>
#include <map>
#include <algorithm>

/** define-for **/

#define REP(i, n) for (int i=0;i<int(n);++i)
#define FOR(i, a, b) for (int i=int(a);i<int(b);++i)
#define DWN(i, b, a) for (int i=int(b-1);i>=int(a);--i)
#define REP_1(i, n) for (int i=1;i<=int(n);++i)
#define FOR_1(i, a, b) for (int i=int(a);i<=int(b);++i)
#define DWN_1(i, b, a) for (int i=int(b);i>=int(a);--i)
#define REP_N(i, n) for (i=0;i<int(n);++i)
#define FOR_N(i, a, b) for (i=int(a);i<int(b);++i)
#define DWN_N(i, b, a) for (i=int(b-1);i>=int(a);--i)
#define REP_1_N(i, n) for (i=1;i<=int(n);++i)
#define FOR_1_N(i, a, b) for (i=int(a);i<=int(b);++i)
#define DWN_1_N(i, b, a) for (i=int(b);i>=int(a);--i)

/** define-useful **/

#define clr(x,a) memset(x,a,sizeof(x))
#define sz(x) int(x.size())
#define see(x) cerr<<#x<<" "<<x<<endl
#define se(x) cerr<<" "<<x
#define pb push_back
#define mp make_pair

/** test **/

#define Display(A, n, m) {                      \
    REP(i, n){                                  \
        REP(j, m) cout << A[i][j] << " ";       \
        cout << endl;                           \
    }                                           \
}

#define Display_1(A, n, m) {                    \
    REP_1(i, n){                                \
        REP_1(j, m) cout << A[i][j] << " ";     \
        cout << endl;                           \
    }                                           \
}

using namespace std;

/** typedef **/

typedef long long LL;

/** Add - On **/

const int direct4[4][2]={ {0,1},{1,0},{0,-1},{-1,0} };
const int direct8[8][2]={ {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int direct3[6][3]={ {1,0,0},{0,1,0},{0,0,1},{-1,0,0},{0,-1,0},{0,0,-1} };

const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const long long INFF = 1LL << 60;
const double EPS = 1e-9;
const double OO = 1e15;
const double PI = acos(-1.0); //M_PI;
const int maxn=555;
int n,m;

struct StoerWagner{
    int mat[maxn][maxn];
    int dis[maxn];
    int S,T;
    int n;
    bool vis[maxn],del[maxn];
    void init(int n){
        memset(mat,0,sizeof(mat));
        this->n=n;
    }
    void addedge(int u,int v,int c){
        mat[u][v]+=c;
        mat[v][u]+=c;
    }
    int search(int ct){
        int tmp,mx,cut;
        memset(vis,0,sizeof(vis));
        memset(dis,0,sizeof(dis));
        T=S=-1;
        for (int i=0;i<n-ct;i++){
            mx=-1;
            for (int j=0;j<n;j++){
                if (!vis[j]&&!del[j]&&dis[j]>mx){
                    mx=dis[j];
                    tmp=j;
                }
            }
            S=T;
            T=tmp;
            cut=mx;
            vis[T]=true;
            for (int j=0;j<n;j++){
                if (!vis[j]&&!del[j]){
                    dis[j]+=mat[T][j];
                }
            }
        }
        return cut;
    }
    int minimumCut(){
        int ans=INF;
        memset(del,0,sizeof(del));
        for (int i=0;i<n-1;i++){
            int cut=search(i);
            if (cut<ans) ans=cut;
            if (ans==0) return 0;
            del[T]=true;
            for (int j=0;j<n;j++){
                if (!del[j]){
                    mat[S][j]+=mat[T][j];
                    mat[j][S]+=mat[T][j];
                }
            }
        }
        return ans;
    }
}solver;

int main()
{
    while (~scanf("%d%d",&n,&m)){
        solver.init(n);
        while (m--){
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            solver.addedge(x,y,z);
        }
        printf("%d\n",solver.minimumCut());
    }
    return 0;
}








posted on 2013-09-07 15:56  电子幼体  阅读(189)  评论(0编辑  收藏  举报

导航