//最小割 Stoer-Wagner 算法
//Etrnls 2007-4-15
//Stoer-Wagner 算法用来求无向图 G=(V, E)的全局最小割。
//算法基于这样一个定理:对于任意s, t V ∈ ,全局最小割或者等于原图的s-t 最小割,或者等于将原图进行 Contract(s,
//t)操作所得的图的全局最小割。
//算法框架:
//1. 设当前找到的最小割MinCut 为+∞
//2. 在 G中求出任意 s-t 最小割 c,MinCut = min(MinCut, c)
//3. 对 G作 Contract(s, t)操作,得到 G'=(V', E'),若|V'| > 1,则G=G'并转 2,否则MinCut 为原图的全局最
//小割
//Contract 操作定义:
//若不存在边(p, q),则定义边(p, q)权值w(p, q) = 0
//Contract(a, b): 删掉点 a, b 及边(a, b),加入新节点 c,对于任意 v V ∈ ,w(v, c) = w(c, v) = w(a, v) + w(b,
//v)
//求 G=(V, E)中任意 s-t 最小割的算法:
//定义w(A, x) = ∑w(v[i], x),v[i] A ∈
//定义 Ax 为在x 前加入 A 的所有点的集合(不包括 x)
//1. 令集合 A={a},a为 V中任意点
//2. 选取 V - A中的 w(A, x)最大的点 x加入集合 A
//3. 若|A|=|V|,结束
//令倒数第二个加入 A的点为 s,最后一个加入 A的点为 t,则s-t 最小割为 w(At, t)
//上述做法(转载) 基于两个事实(来自论文a simple and fast min-cut algorithm)
//λG= min(λG(u, v), λG/u~v).
//For each MA-order v1,...,vn of the undirected,weighted Graph G = (V, E),
//the cut({v1,...,vn−1},{vn})is a minimum vn–vn−1-cut
#include<cstdio> //代码很难看懂的原因是代码本身不会明确的告诉你代码应用的不明显成立的定理
#include<cstring> //代码的最初出处很难找到了
using namespace std;
const int MAX = 105;
const int INF = 1<<30;
int n, m, s, t, minCut;
int dis[MAX], map[MAX][MAX];
bool vis[MAX], set[MAX];
void search(){
int i, j, ma, tmp;
memset(vis, 0, sizeof(vis));
memset(dis, 0, sizeof(dis));
s = t = -1;
for(i = 0; i < n; i ++){ //加入点的过程
for(ma = -INF, j = 0; j < n; j ++){
if(!set[j] && !vis[j] && dis[j] > ma){
tmp = j;
ma = dis[j];
}
}
if(t == tmp) return;
s = t, t = tmp;
minCut = ma;
vis[tmp] = true;
for(j = 0; j < n;j ++){
if(!set[j] && !vis[j]) dis[j] += map[tmp][j];
}
}
return;
}
int Stoer_Wagner(){
int i, j, ans = INF;
memset(set, 0, sizeof(set));
for(i = 0; i < n-1; i ++){ //缩点n-1次
search();
if(minCut < ans) ans = minCut;
if(ans == 0) return 0;
set[t] = true; //set是被缩了的点
for(j = 0; j < n; j ++)
if(!set[j]){
map[s][j] += map[t][j];
map[j][s] += map[j][t];
}
}
return ans;
}
int main(){
int u, v ,w;
while(scanf("%d%d",&n,&m)!=EOF){
memset(map, 0, sizeof(map));
while(m --){
scanf("%d%d%d",&u,&v,&w);
map[u][v] += w;
map[v][u] += w;
}
printf("%d\n",Stoer_Wagner());
}
return 0;
}
#include<cstdio> //上述代码的常数优化
#include<cstring>
const int N=102;
int p[N],dis[N],map[N][N];
bool vis[N];
int mincut(int n){
int ret=0x7fffffff;
for(int i=0;i!=n;++i) p[i]=i;
while(n>1){
int t=1,s=0;
for(int i=1;i!=n;++i){
dis[p[i]]=map[p[0]][p[i]];
if(dis[p[i]]>dis[p[t]]) t=i;
}
memset(vis,0,sizeof(vis));
vis[p[0]]=true;
for(int i=1;i<n;++i){
if(i==n-1){
if(ret>dis[p[t]]) ret=dis[p[t]];
for(int j=0;j!=n;++j){
map[p[j]][p[s]]+=map[p[j]][p[t]];
map[p[s]][p[j]]=map[p[j]][p[s]];
}
p[t]=p[--n];
}
vis[p[t]]=true;
s=t;t=-1;
for(int j=1;j!=n;++j){
if(!vis[p[j]]){
dis[p[j]]+=map[p[j]][p[s]];
if(t==-1||dis[p[j]]>dis[p[t]]) t=j;
}
}
}
}
return ret;
}
int main(){
int n ,m ,u, p ,w;
while(scanf("%d%d",&n,&m)!=EOF){ //节点为0~~~~n-1
memset(map, 0, sizeof(map));
while(m --){
scanf("%d%d%d",&u,&p,&w);
map[u][p] += w;
map[p][u] += w;
}
printf("%d\n",mincut(n));
}
return 0;
}