POJ 2914 - Minimum Cut - 全局最小割,Stoer-Wagner算法

题目大意:给定一个N个点、M条边的无向带权图,边的权值均为正整数。若要使它变成非连通图,需要移除的边总权值最小是多少?

N≤500,图中不存在自环,但可能有重边(这里题意没交代清楚)。

Stoer-Wagner算法裸题。英文维基:https://en.wikipedia.org/wiki/Stoer%E2%80%93Wagner_algorithm

该算法的思想之一是:对于一个无向连通图,选定某两点s,t,以及该图的一个s-t割C,则“C是该图的全局最小割”是“C是s-t的最小割”的充分不必要条件。

也就是说,如果我们求得了某两点s-t的最小割C,那么全局最小割要么就是C,要么就是另一个让s,t保持连通的割。因此我们可以在得到s-t最小割之后,将这两个节点合并,然后继续求另外某两点的最小割。

合并节点的方法:对于s,t以外的每个节点u,设边(s,u)的权值为v1(如果该边不存在则v1=0),边(t,u)的权值为v2,那么合并后的新节点与u之间连一条边,权值为v1+v2(当然如果v1=0且v2=0则不用连这条边,反正权值为0的边可以随便删)。

该算法的流程为:

def MinCutPhase(a)
    点集S ← a
    while |S| < 当前图中点的个数
        记节点u与S中所有节点之间边的权值之和为w(u),选取不属于S且w(u)最大的节点u0
        将u0加入S
    y = 最后一个加入S的节点
    x = 倒数第二个加入S的节点
    合并点x,y
    return 合并前的w(y)

def MinCut()
    ans = +while 图中点的个数 > 1
        任选某个点a
        ans = min(ans, MinCutPhase(a))
    return ans

可以证明在MinCutPhase过程中,w(y)就是x-y最小割的总权值(详见维基,说实话我也看不懂 ̄□ ̄)。

求w(u)的过程和最小生成树的Prim算法很类似。维护一个cost数组,对于每个新加入S的节点,更新与它相连的节点的cost值。

时间复杂度:单次MinCutPhase过程的复杂度与Prim算法相同。优化前为O(N^2),如果用邻接表+堆优化,可以将复杂度降到O(M + N logN)。

MinCutPhase过程共执行了O(N)次,因此总的复杂度为O(N^3)或O(MN + N^2 logN)。

O(N^3)的AC代码:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #include <climits>
  5 
  6 using namespace std;
  7 
  8 const int maxN = 500 + 5;
  9 
 10 int dist[maxN][maxN]; //edge (u, v) does not exist if dist[u][v] == 0
 11 int cost[maxN];
 12 bool erased[maxN]; //whether a vertex is erased after merging
 13 bool vis[maxN]; //whether a vertex is visited during minCutPhase process
 14 int N, M;
 15 
 16 bool input()
 17 {
 18     if (scanf("%d%d", &N, &M) == EOF)
 19         return false;
 20 
 21     memset(dist, 0, sizeof(dist));
 22     for (int u, v, w, i = 0; i < M; i++)
 23     {
 24         scanf("%d%d%d", &u, &v, &w);
 25         dist[u][v] = (dist[v][u] += w);
 26     }
 27     return true;
 28 }
 29 
 30 ///@brief: merge u and v, let dist[u][i] += dist[v][i], dist[v][i] = 0; (i.e. "erase" node v)
 31 void mergeVertex(int u, int v)
 32 {
 33     for (int i = 0; i < N; i++)
 34     {
 35         dist[i][u] = (dist[u][i] += dist[v][i]);
 36         dist[v][i] = dist[i][v] = 0;
 37     }
 38     erased[v] = true;
 39 }
 40 
 41 ///@brief: get the minimum s-t cut, and merge s and t
 42 ///@param remN: number of remaining vertices
 43 int minCutPhase(int remN)
 44 {
 45     memset(vis, 0, sizeof(vis));
 46     memset(cost, 0, sizeof(cost));
 47 
 48     int cur = static_cast<int>(find(erased, erased + N, false) - erased); //find a non-erased node
 49     int pre = cur;
 50     vis[cur] = true;
 51 
 52     for (int i = 1; i < remN; i++) //repeat (remN - 1) times
 53     {
 54         for (int to = 0; to < N; to++)
 55             cost[to] += dist[cur][to];
 56 
 57         int maxCost = 0;
 58         int maxCostVertex = 0;
 59         for (int j = 0; j < N; j++)
 60         {
 61             if (!vis[j] && cost[j] > maxCost)
 62             {
 63                 maxCost = cost[j];
 64                 maxCostVertex = j;
 65             }
 66         }
 67 
 68         if (maxCost == 0) //not updated at all, indicating that the graph is not connected
 69             return 0;
 70 
 71         pre = cur;
 72         cur = maxCostVertex;
 73         vis[cur] = true;
 74     }
 75 
 76     mergeVertex(pre, cur);
 77     return cost[cur];
 78 }
 79 
 80 int minCut()
 81 {
 82     memset(erased, 0, sizeof(erased));
 83 
 84     int ans = INT_MAX;
 85     for (int remN = N; remN > 1; --remN)
 86     {
 87         int t = minCutPhase(remN);
 88         if (t == 0)
 89             return 0;
 90         ans = min(ans, t);
 91     }
 92     return ans;
 93 }
 94 
 95 int main()
 96 {
 97     while (input())
 98         printf("%d\n", minCut());
 99     return 0;
100 }

 

 

posted @ 2018-02-19 15:19  Onlynagesha  阅读(354)  评论(0编辑  收藏  举报