poj 3164(最小树形图)

传送门
最小树形图定义
在给定的有向带权图中指定一个点为root,求一棵以root为根的有向生成树T使得T中边权之和最小。解决此类问题的第一个算法是朱刘算法,时间复杂度O(VE)。
算法流程
1.找到除了指定的root以外所有点的最小入边以及对应的前驱,分别记为in[i]/pre[i]
2.如果操作1执行完后仍有in[i]等于初值INF,说明图不连通,不存在最小树形图
3.找出图中所有环,重新编号
4.对于每个环,更新环外的点到环上的点的距离
5.重复操作3和4直到不再有环存在

模板题。。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=105,MAXM=1e4+5;
const double INF=0x3f3f3f3f;
int n,m;
int pre[MAXN],id[MAXN],col[MAXN];
double in[MAXN];
struct EDGE {
    int u,v;
    double w;
}e[MAXM];
struct Node {
    double x,y;
}nd[MAXN];
inline double dis(Node a,Node b) {
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double dmst(int root) {
    double ret=0;
    while (1) {
        for (int i=1;i<=n;++i) in[i]=INF;
        for (int i=0;i<m;++i) {
            int u=e[i].u,v=e[i].v;
            if (u^v&&e[i].w<in[v])
                in[v]=e[i].w,pre[v]=u;
        }
        for (int i=1;i<=n;++i)
            if (i^root&&in[i]==INF) return -1;
        int tn=0;
        memset(id,-1,sizeof(id));
        memset(col,-1,sizeof(col));
        in[root]=0;
        for (int i=1;i<=n;++i) {
            ret+=in[i];
            int v=i;
            while (col[v]!=i&&id[v]==-1&&v^root)
                col[v]=i,v=pre[v];
            if (v^root&&id[v]==-1) {
                id[v]=++tn;
                for (int p=pre[v];p^v;p=pre[p])
                    id[p]=tn;
            }
        }
        if (!tn) break;
        for (int i=1;i<=n;++i)
            if (id[i]==-1) id[i]=++tn;
        for (int i=0;i<m;++i) {
            int v=e[i].v;
            e[i].u=id[e[i].u];
            e[i].v=id[e[i].v];
            if (e[i].u^e[i].v) e[i].w-=in[v];
        }
        n=tn;
        root=id[root];
    }
    return ret;
}
int main() {
//  freopen("poj 3164.in","r",stdin);
    while (~scanf("%d%d",&n,&m)) {
        for (int i=1;i<=n;++i) scanf("%lf%lf",&nd[i].x,&nd[i].y);
        for (int i=0;i<m;++i) {
            int a,b;
            scanf("%d%d",&a,&b);
            e[i].u=a,e[i].v=b;
            if (a^b) e[i].w=dis(nd[a],nd[b]);
        }
        double ans=dmst(1);
        if (ans!=-1) printf("%.2f\n",ans);
        else puts("poor snoopy");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值