Directed_MST 最小树形图
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;