一道最小树形图模板题,学习了一下
http://hi.baidu.com/lydrainbowcat/item/5fbae3fb9c159c5ec8f33753
以下转自该博客:
题目大意:给定一个有向图,根节点已知,求该有向图的最小树形图。最小树形图即有向图的最小生成树,定义为:选择一些边,使得根节点能够到达图中所有的节点,并使得选出的边的边权和最小。
题目算法:朱-刘算法(即由中国人朱永津和刘振宏共同发明的算法)。
算法步骤如下:(本文不再证明,参考下面给出的我自己画的一个图即可理解)
1.判断图的连通性,若不连通直接无解,否则一定有解。
2.为除了根节点以外的所有点选择一个权值最小的入边,假设用pre数组记录前驱,f数组记录选择的边长,记所选边权和为temp。
3.(可利用并查集)判断选择的的边是否构成环,若没有则直接ans+=temp并输出ans,若有,则进行下一步操作。
4.对该环实施缩点操作,设该环上有点V1,V2……Vi……Vn,缩成的点为node ,对于所有不在环中的点P进行如下更改:
(1) 点P到node的距离为min{a[p,Vi]-f[Vi]} (a为边集数组)
(2)点node到p的距离为min{a[Vi,p]}
操作(1)的理解:先假设环上所有边均选上,若下次选择某一条边进入该环,则可以断开进入点与进入点的前驱之间的边,即断开F[进入点],所以等效为直接把a[p,node]赋值为min{a[p,Vi]-f[Vi]}。
特别提醒:本题有自环,可以提前删掉,因为它没有用。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1005;
const int maxm = 40005;
const int inf = 0x3f3f3f3f;
struct node
{
int from;
int v;
int len;
}edge[maxm];
int in[maxn],pre[maxn],id[maxn],vis[maxn];
int dir_mst(int root,int n,int m)
{
int ans=0;
while(1)
{
//先找出所有点的最小入边
/*memset(in,inf,sizeof(in)); //这样写会报错!*/
for(int i=0;i<n;++i)
{
in[i]=inf;
}
for(int i=0;i<m;++i)
{
int u=edge[i].from,v=edge[i].v;
if(edge[i].len<in[v]&&u!=v)
{
pre[v]=u;in[v]=edge[i].len;
}
}
for(int i=0;i<n;++i)
{
if(i==root)
{
continue;
}
if(in[i]==inf)
{
return -1; //如果某点入度为零,必定找不到
}
}
//检查这些边是否构成了环
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root]=0;
int cnt=0;
for(int i=0;i<n;++i)//标记环
{
ans+=in[i];
int v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)
{
vis[v]=i;
v=pre[v];
}
if(v!=root&&id[v]==-1)//缩点
{
for(int u=pre[v];u!=v;u=pre[u])
{
id[u]=cnt;
}
id[v]=cnt++;
}
}
if(cnt==0)
{
break;//无环
}
for(int i=0;i<n;++i)
{
if(id[i]==-1)
{
id[i]=cnt++;
}
}
//建立新图
for(int i=0;i<m;++i)
{
int u=edge[i].from,v=edge[i].v;
edge[i].from=id[u];
edge[i].v=id[v];
if(id[u]!=id[v])
{
edge[i].len-=in[v];
}
}
n=cnt;
root=id[root];
}
return ans;
}
int main()
{
int cases,u,v,n,m,w,t = 1;
scanf("%d",&cases);
while(cases--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
scanf("%d%d%d",&edge[i].from,&edge[i].v,&w);
if(edge[i].from==edge[i].v) edge[i].len = inf;
else edge[i].len = w;
}
int ans = dir_mst(0,n,m);
if(ans==-1 ) printf("Case #%d: Possums!\n",t++);
else printf("Case #%d: %d\n",t++,ans);
}
return 0;
}