POJ--2914(Stoer_Wagne,全局最小割)
2015-05-17 22:07:10
题目:题意很浅显:求全局最小割。
思路:Stoer_Wagner 启蒙题。参考博客文章:(1),(2)
有两种打法:
(1)第一种,思路清晰但是跑得比较慢。8000+MS
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 510; int g[MAXN][MAXN]; ll wg[MAXN]; bool vis[MAXN],del[MAXN]; int N,M,K; int Solve(int &st,int &ed){ st = ed = -1; memset(vis,0,sizeof(vis)); //生成树前的必要初始化 memset(wg,0,sizeof(wg)); int min_cut = -1,p = 0; for(int i = 0; i < N; ++i){ int tmax = -1; //找后继最大的权点 for(int j = 0; j < N; ++j) if(!del[j] && !vis[j]){ if(p == -1 || wg[j] > tmax){ p = j; tmax = wg[j]; } } if(p == ed) return min_cut; //点没变,无法扩展 st = ed; ed = p; min_cut = tmax; vis[p] = 1; for(int j = 0; j < N; ++j) //拓展点后更新权和数组 if(!del[j] && !vis[j]) wg[j] += g[p][j]; } return min_cut; } int SW(){ int ans = INF,st,ed; memset(del,0,sizeof(del)); for(int i = 1; i < N; ++i){ //删点N-1次 int min_cut = Solve(st,ed); //传起点、终点 ans = min(ans,min_cut); if(ans == 0) return 0; //最小割为0,直接剪枝,返回0 del[ed] = 1; for(int j = 0; j < N; ++j) if(!del[j] && j != st){ g[st][j] += g[ed][j]; //删点后合并边权 g[j][st] += g[j][ed]; } } return ans; } int main(){ int a,b,c; while(scanf("%d%d%d",&N,&M,&K) != EOF){ memset(g,0,sizeof(g)); for(int i = 1; i <= M; ++i){ scanf("%d%d%d",&a,&b,&c); g[a][b] += c; g[b][a] += c; } printf("%d\n",SW()); } return 0; }
(2)第二种
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <string> #include <iostream> #include <algorithm> using namespace std; #define getmid(l,r) ((l) + ((r) - (l)) / 2) #define MP(a,b) make_pair(a,b) #define PB(a) push_back(a) typedef long long ll; typedef pair<int,int> pii; const double eps = 1e-8; const int INF = (1 << 30) - 1; const int MAXN = 510; int N,M; int g[MAXN][MAXN]; int v[MAXN],wg[MAXN],vis[MAXN]; int SW(int n){ int res = -1; for(int i = 0; i < n; ++i) v[i] = i; //点标号的马甲,一开始都是自己。 while(n > 1){ memset(vis,0,sizeof(vis)); //标记数组 memset(wg,0,sizeof(wg)); //权和数组 int pre = 0; //起点为0号点 vis[pre] = 1; //标记起点,虽然实际用处,但是防止错误 for(int i = 1; i < n; ++i){ int p = -1; for(int j = 1; j < n; ++j) if(!vis[v[j]]){ //寻找下个拓展点 wg[v[j]] += g[v[pre]][v[j]]; //下个点的权和加上边权 if(p == -1 || wg[v[j]] > wg[v[p]]) p = j; //寻找wg值最大的点 } vis[v[p]] = 1; //标记下个点已经遍历 if(i == n - 1){ //最后个点,需要合并 if(res == -1) res = wg[v[p]]; else res = min(res,wg[v[p]]); //更新res,取最小割 for(int j = 0; j < n; ++j){ g[v[pre]][v[j]] += g[v[p]][v[j]]; //边权合并到倒数第二个点 g[v[j]][v[pre]] += g[v[j]][v[p]]; } v[p] = v[--n]; //删除k点,把n-1点移到k点位置 } pre = p; //点位置变换,勿忘! } } return res; } int main(){ int a,b,c; int K; while(scanf("%d%d%d",&N,&M,&K) != EOF){ //点数、边数 memset(g,0,sizeof(g)); for(int i = 1; i <= M; ++i){ scanf("%d%d%d",&a,&b,&c); //输入边 g[a][b] += c; g[b][a] += c; } printf("%d\n",SW(N)); //注意要给Stoer_Wagner传一个初始点数 } return 0; }