List
Knowledge
求一个图中遍历到每个点的方案中的最小边权和,显然n-1条边,即一颗树即可。 最小生成树?当然这里不是的,这里的最小树形图算法是针对有向图的。 最小树形图的第一个算法是1965年朱永津和刘振宏提出的复杂度为O(VE)的算法。简称朱刘算法。
1986年, Gabow, Galil, Spencer和Tarjan提出了一个复杂度更好的实现,其时间复杂度为O(E+VlogV)。%啊,又是Tarjan大爷。好像并没有翻到详细的记载,一般用不到。应该有点复杂吧。
下面介绍一下ZLEdmonds:
简述:记pre[i]为连到i点所有前驱中边权最小的一个 可以理解如果没有环,那么这个方案即为所求 如果有环肯定就有bug,因为点显然不连通 找环,先默认环上的边都加入答案(后面重构边权会导致最后方案把环上一条边去掉) 缩点将其与其它点连边边权改为w-=w[i→pre[i]],这样最后答案如果连了这条边,环上的点就不要再来一次。 这样缩环后重新找pre,直到不存在环 这样重新构图后,虽然开始有环,但重构的边权会使最后删掉环上的某条边 保证了最后肯定是一个树形图,而且还是最小的
模板
struct Directed_MST{ static const int N=110,M=N*N; static const int/double INF=1e9; int m,n,root; int/double w[M],s[N],ans; int u[M],v[M],pre[N],id[N],f[N]; void Combine(){ for(int i=1;i<=m;i++){ int gg=v[i];u[i]=id[u[i]];v[i]=id[v[i]]; if(u[i]!=v[i])w[i]-=s[gg]; } } void ZLEdmonds(){ ans=0; while(true){ for(int i=1;i<=n;i++)s[i]=INF; for(int i=1;i<=m;i++) if(w[i]<s[v[i]]&&u[i]!=v[i])s[v[i]]=w[i],pre[v[i]]=u[i]; int cnt=1; memset(id,-1,sizeof(id)); memset(f,0,sizeof(f)); s[root]=0; for(int i=1;i<=n;i++){ ans+=s[i];int x=i; while(f[x]!=i&&x!=root)f[x]=i,x=pre[x]; if(x!=root&&id[x]==-1){ for(int o=pre[x];o!=x;o=pre[o])id[o]=cnt; id[x]=cnt++; } } if(cnt==1)break; for(int i=1;i<=n;i++)if(id[i]==-1)id[i]=cnt++; Combine();n=cnt-1;root=id[root]; } cout<<ans; } }ZL;
Practice
Bzoj1179 Apio2009 Atm(2016-09-20 19:54)
参考资料
Wiki Minimum spanning tree
Wiki Edmonds’ algorithm
时间: 2024-10-13 08:50:58