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; }