【POJ3164】Command Network 最小树形图模板题 重修版

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44935891");
}

我以前的版本

算法构造过程以及傻叉代码+弱版注释见以前博客
http://blog.csdn.net/vmurder/article/details/38819711

最小树形图:

名词解释:

        其实就是有向图的最小生成树,然后需要有一个根(一般默认为1),如果是无根最小树形图,我们可以牺牲时间复杂度, O(n) 枚举根跑最小树形图。

算法流程:

  1. 对于一张有向图,除了根以外每个点都找一条权值最小的入边(称之为 [最小弧] )
  2. 这样就可能构成了一片由基环树组成的森林,然后我们把每个环都缩点,并根据点的新编号重建图。但是如果没有环(所有点与根连通),则最小树形图已经建成,跳出流程。

如果计算边权:

每次将所有点(根除外)最小弧长度累加,重建图时与x相连的边权值全部减去此最小弧长度,这样就保证了最后一个点的流程中加得的所有最小弧长度等于最后一次加的边的原长度。

代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 0x3f3f3f3f
#define N 200
#define M 50000
using namespace std;
struct Fiona
{
    double x,y;
}s[N];
struct Syndra
{
    int u,v;
    double w;
}e[M];
int pre[N],f[N],vis[N];
double l[N];
double dist(Fiona A,Fiona B)
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Directed_MST(int root,int n,int m)
{
    int i,j,k;
    int u,v,cnt;
    double ans=0;
    while(1)
    {
        for(i=1;i<=n;i++)f[i]=vis[i]=0,l[i]=inf;
        for(i=1;i<=m;i++)
        {
            u=e[i].u;v=e[i].v;
            if(l[v]>e[i].w)pre[v]=u,l[v]=e[i].w;
        }
        for(i=1;i<=n;i++)if(l[i]==inf&&i!=root)return -1;
        l[root]=cnt=0;
        for(i=1;i<=n;i++)
        {
            ans+=l[i];
            if(vis[i])continue;
            for(v=i;!vis[v]&&v!=root;vis[v]=i,v=pre[v]);
            if(v!=root&&vis[v]==i)
            {
                f[v]=++cnt;
                for(u=pre[v];u!=v;u=pre[u])f[u]=cnt;
            }
        }
        if(!cnt)break;
        for(i=1;i<=n;i++)if(!f[i])f[i]=++cnt;
            n=cnt,cnt=0,root=f[root];
        for(i=1;i<=m;i++)
        {
            u=e[i].u,v=e[i].v;
            if(f[u]!=f[v])
            {
                e[++cnt].u=f[u];
                e[cnt].v=f[v];
                e[cnt].w=e[i].w-l[v];
            }
        }
        m=cnt;
    }
    return ans;
}
int main()
{
    freopen("test.in","r",stdin);
    int i,n,m;
    double ans;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)scanf("%lf%lf",&s[i].x,&s[i].y);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&e[i].u,&e[i].v);
            if(e[i].u!=e[i].v)e[i].w=dist(s[e[i].u],s[e[i].v]);
            else i--,m--;
        }
        ans=Directed_MST(1,n,m);
        if(ans==-1)printf("poor snoopy\n");
        else printf("%.2f\n",ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值