poj 2914 最小割 Stoer-Wagner 算法

POJ - 2914点我点我
Time Limit: 10000MS Memory Limit: 65536KB 64bit IO Format: %lld & %llu

Status

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 ≤ MN × (N − 1) ⁄ 2) in one line, whereN is the number of vertices. Following areM lines, each line containsM integersA,B andC (0 ≤ A, B <N, AB, C > 0), meaning that there C edges connecting verticesA andB.

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


题意:给出n个点,m条无向边的图,要求删除总边权最小的一个边集,使图不连通,即最小割问题


题解:Stoer-Wagner 算法

      有一个定理:最小割等于最大流。

     不过本题不能用最大流的算法,不然会TLE,学习了下Stoer-Wagner 算法,代码不长,理解上稍稍难一些,算法过程如下:

     

     0.设ans = INF

     1.把所有的点加入, 选取任意一个点作为标准点,把其他的点的dis[]赋值成标准点与它本身的距离,并找离它最大的dis对应的点p.

     2.用p更新其他的未处理过的所有的点的dis[],把它们的dis[]加上它们与p的距离,并再次找到新的p(已经处理过的点不再算入),并重复2过程,直到把点处理完。

     3.然后把2过程的最后两个点之间的dis[]值用于更新答案,并且把这两个点缩成一个点,缩好点后,再更新图,假设要缩u,v变成v,  把所有到v边权加上u,v之间的边权,再返回第1步,直到缩得只剩下一个点


模拟:(http://blog.sina.com.cn/s/blog_700906660100v7vb.html)

 

poj 2914代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>

using namespace std;

#define MAXN (500+5)
#define INF 0x3f3f3f3f
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)

int n, m;
int G[MAXN][MAXN], v[MAXN], dis[MAXN];
bool vis[MAXN];

int stoer_wagner(){
	For(i, 1, n) v[i] = i;
	int ret = INF;
	
	while(n > 1){
		int p = 0, last = 0;
		
		For(i, 2, n){
			dis[v[i]] = G[v[1]][v[i]];
			if(dis[v[i]] > dis[v[p]]) p = i;
		}
		For(i, 2, n) vis[v[i]] = false;
		vis[v[1]] = true;
		
		For(i, 2, n){
			if(i == n){
				ret = min(ret, dis[v[p]]);
				For(j, 1, n) G[v[j]][v[last]] = G[v[last]][v[j]] += G[v[j]][v[p]];
				v[p] = v[n--];
			}
			
			vis[v[last=p]] = true; p = 0;
			For(j, 2, n)
			  if(!vis[v[j]]) {
			      dis[v[j]] += G[v[last]][v[j]];//更新dis
			      if(!p || dis[v[p]] < dis[v[j]]) p = j; 
			  }
		}
	}
	
	return ret;
}

int main(){
	while(scanf("%d%d", &n, &m) != EOF){
		Set(G, 0);
		
		For(i, 1, m){
			int u, v, dis;
			scanf("%d%d%d", &u, &v, &dis);
			++u, ++v;
			G[u][v] += dis; G[v][u] += dis;
		}
		
		printf("%d\n", stoer_wagner());
	}
	
	return 0;
}



  


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值