Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 8379 | Accepted: 3516 | |
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 integersA, 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 12
题意:给出一个N个点、M对联系关系的无向图。其中每对联系关系a b c代表a和b之间有c条边。问你使图不连通至少去掉多少条边。
时间复杂度分析:最大流增广路算法需要O((N^2)M),当图最复杂时M 近似= N^N,那么复杂度为O(N^5)。加上枚举源、汇,绝对TLE。最高标号预进流算法我不会,再说这个最牛的算法也会TLE o(╯□╰)o
全局最小割Stoer-Wagner算法实现 时间复杂度O(N^3) 优化后可以达到O(N^2log(N))
用wage数组记录点的连通度,vis数组标记点是否在集合里面,In数组表示点被其它点合并。
分两步
一:找到S - T的最小割Mincut,其中S 和 T为最后并入集合的两个点。
1,初始化数组vis 和 wage;
2,遍历所有不在集合且没有被合并的点,找到最大wage值的点Next,并记录Mincut、S和T;
3,Next并入集合,叠加与Next相连的所有点(不在集合 且 没有被合并),更新这些点的wage值;
4,重复操作2和3一共N次 或者 找不到新的Next值时 跳出,返回Mincut;
二、找全局最小割ans,需要重复第一步N-1次,因为每次合并一个点,最多合并N-1个点;
1,每次对返回的Mincut,更新ans = min(ans, Mincut),当然ans为0时,说明图不连通;
2,把点T合并到S点,操作有:对 所有没有被合并的点j,Map[S][j] += Map[T][j]。
证明讲解:
AC代码:
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 500+10 #define INF 0x3f3f3f3f using namespace std; int Map[MAXN][MAXN]; bool vis[MAXN];//是否已并入集合 int wage[MAXN];//记录每个点的连通度 bool In[MAXN];//该点是否已经合并到其它点 int N, M; void getMap() { memset(Map, 0, sizeof(Map)); int a, b, c; for(int i = 0; i < M; i++) { scanf("%d%d%d", &a, &b, &c); a++, b++; Map[a][b] += c; Map[b][a] += c; } } int S, T;//记录每次找s-t割 所遍历的最后两个点 int work() { int Mincut;//每一步找到的s-t割 memset(wage, 0, sizeof(wage)); memset(vis, false, sizeof(vis)); int Next; for(int i = 1; i <= N; i++) { int Max = -INF; for(int j = 1; j <= N; j++) { if(!In[j] && !vis[j] && Max < wage[j])//找最大的wage值 { Next = j; Max = wage[j]; } } if(Next == T) break;//找不到点 图本身不连通 vis[Next] = true;//标记 已经并入集合 Mincut = Max;//每次更新 S = T, T = Next;// 记录前、后点 for(int j = 1; j <= N; j++)//继续找不在集合 且 没有被合并过的点 { if(In[j] || vis[j]) continue; wage[j] += Map[Next][j];//累加 连通度 } } return Mincut; } int Stoer_wagner() { memset(In, false, sizeof(In)); int ans = INF; for(int i = 0; i < N-1; i++) { ans = min(ans, work()); if(ans == 0) return 0;//本身不连通 In[T] = true; for(int j = 1; j <= N; j++)//把T点合并到S点 { if(In[j]) continue;//已经合并 Map[S][j] += Map[T][j]; Map[j][S] += Map[j][T]; } } return ans; } int main() { while(scanf("%d%d", &N, &M) != EOF) { getMap(); printf("%d\n", Stoer_wagner()); } return 0; }