zoukankan      html  css  js  c++  java
  • POJ 2914

    题目大意:给定一个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
    ", minCut());
     99     return 0;
    100 }

  • 相关阅读:
    [NOIP2011]选择客栈
    [学习笔记]字符串的一些基本操作
    [学习笔记]树链剖分
    [宁波集训]0827Day1
    [POI2015]LOG(树状数组)
    [学习笔记]树形dp
    货车运输(最大生成树+倍增LCA)
    POJ 3617 Best Cow Line 贪心算法
    C++ STL next_permutation() prev_permutation(a,a+n)用法。
    POJ 2386 Lake Counting dfs
  • 原文地址:https://www.cnblogs.com/Onlynagesha/p/8453824.html
Copyright © 2011-2022 走看看