题解:Stoer_Wagner算法。
Stoer_Wagner算法:不断进行缩点,在缩点过程中更新答案,类似于最小树形图。
缩点步骤:
写一棵苦力怕树:
http://blog.csdn.net/vmurder/article/details/42240877
(写最大生成树的肯定是污得不行)
然后会有一个点x最后才加入到A集合(vis==true),还有一个点y倒数第二个加入到A集合。
这时候把答案用dis[x]更新一下:ans=min(ans,dis[x]);
然后把x和y缩成一个点。
网上有些博客是把x和x的前驱缩成一个点,这是错的。
你写的都不是最大生成树,所有的边都对点x有贡献,怎么可能有前驱?!!
然后重复操作,直到整个图被缩成一个点。
返回ans。
细节:缩点时如何把边也缩一起?
对于入边,只需要一个并查集就可以了,
而对于出边,不妨用链表把并查集里的点全部存下来。
我的next数组和final数组就是链表、、
时间复杂度:
缩点时间复杂度等同于dijkstra/prim
然后会缩n次
加了堆优化O(n^2logn)
没加就是(n^3)
但是因为一些常数之类的问题,稠密图不要加堆优化。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 510 #define M 10500 #define inf 0x3f3f3f3f using namespace std; struct KSD { int u,v,len,next; }e[M<<1]; int head[N],cnt; inline void add(int u,int v,int len) { cnt++; e[cnt].v=v; e[cnt].len=len; e[cnt].next=head[u]; head[u]=cnt; } int n,m; int dis[N],pre[N]; int next[N],final[N]; bool vis[N]; int f[N];int find(int x){return f[x]==x?x:f[x]=find(f[x]);} inline void init() { int i,j,k; memset(head,0,sizeof(head)); for(cnt=1,i=1;i<=n;i++)f[i]=next[i]=final[i]=i; } inline int Stoer_Wagner() { int ans=inf; int i,j,_n,r; int u,v,temp,lastu=0; for(_n=n;_n>1;_n--) { memset(dis,0,sizeof(dis)); memset(vis,0,sizeof(vis)); for(r=1;r<=_n;r++) { lastu=u; temp=-1; for(i=1;i<=n;i++)if(f[i]==i&&!vis[i]) { if(temp<dis[i])temp=dis[i],u=i; } vis[u]=1; bool flag=0; for(j=u;;j=next[j]) { if(j==final[u])flag=1; for(i=head[j];i;i=e[i].next) { v=find(e[i].v); if(v!=u)dis[v]+=e[i].len; } if(flag)break; } } ans=min(ans,dis[u]); f[u]=lastu; next[final[lastu]]=u; final[lastu]=final[u]; } return ans; } int main() { int i,j,k; int a,b,c; scanf("%d%d",&n,&m); init(); while(m--) { scanf("%d%d%d",&a,&b,&c); add(a,b,c); add(b,a,c); } printf("%d\n",Stoer_Wagner()); return 0; } </span>
这里给一组数据:
3 7 1 3 13 3 2 13 3 1 19 2 3 1 2 3 11 1 2 19 2 3 12