UVA 11183 (最小树形图)

一道最小树形图模板题,学习了一下

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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值