求有固定根的最小树形图的算法
算法步骤:
(1)求最短弧集:除了根节点外,找到所有其他的节点最小边权的入边(用in数组记录到达改点的最小边权,用pre数组记录其父节点)
(2)检验生成的集合中是否存在有向圈,有的话进行步骤3,没有进行步骤4,假如除了根节点外有的节点是孤立的,也就是没有弧指向他,不存在最小树形图;
(判断方法:利用pre数组以每个点进行枚举搜索)
(3)把有向环缩成一个点,形成(U1,U2,U3,U4.....Un')共n‘个点定义:不在一个环中的两个点分别为id[u],id[v],边权是w[u][v]-=in[v];然后进行步骤1;
(4)ans就是答案:
程序:
int mini_tree(int root,int n,int m)//分别是树根,节点数,边数,序号从1开始 { int ans=0; int i,u; while(1) { for(i=1;i<=n;i++) in[i]=inf; for(i=1;i<=m;i++) { int u=edge[i].u; int v=edge[i].v; if(edge[i].w<in[v]&&u!=v) { in[v]=edge[i].w; pre[v]=u; } }//找最小的入边 for(i=1;i<=n;i++) { if(i==root)continue; ans+=in[i];//把边权加起来 if(in[i]==inf)//如果存在没有入弧的点则不存在最小树形图 return -1; } memset(id,-1,sizeof(id)); memset(use,-1,sizeof(use)); int cnt=0; for(i=1;i<=n;i++)//枚举每个点,搜索找环 { int v=i; while(v!=root&&use[v]!=i&&id[v]==-1) { use[v]=i; v=pre[v]; } if(v!=root&&id[v]==-1)//当找到环的时候缩点编号 { ++cnt; id[v]=cnt; for(u=pre[v];u!=v;u=pre[u]) id[u]=cnt; } } if(cnt==0)//如果没有环结束程序 break; for(i=1;i<=n;i++)//把余下的不在环里的点编号 if(id[i]==-1) id[i]=++cnt; for(i=1;i<=m;i++)//建立新的图 { int u=edge[i].u; int v=edge[i].v; edge[i].u=id[u]; edge[i].v=id[v]; if(edge[i].u!=edge[i].v) edge[i].w-=in[v]; } n=cnt;//更新节点数和根节点的编号 root=id[root]; } return ans; }