HDU 5723 Abandoned country(2016 Multi-University Training Contest 1的1001题)

本文介绍了如何使用Kruskal算法解决HDU 5723问题,通过构建最小生成树来计算图中任意两点间距离的数学期望。文章详细解释了从理解题目到实现算法的全过程,并分享了一些调试经验和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=5723


感悟:

本来已经学过也练过一些图论基础的题目,但是因为学长没在VJ上挂最小生成树的题直接挂了最短路和最大流的题,所以没做过最小生成树的题,但是在学了离散数学还是了解最小生成树的一些概念。拿到题,仔细思考,一开始队友说是哈密顿图,于是我翻了翻kuangbin模板,最后感觉是最小生成树。花10分钟敲了一下Kruskal算法,第一个输出值直接就可以了。其中百度了一下“树中任意两点之间距离的数学期望”,没找到,但找到了“树中任意两点之间距离的平均值”,看了一会感觉没啥用 =- =,所以最终坑在了上面


分析:

看了官方题解,仔细回味,才发现他么期望就是数学期望,两个是一样的。。。我当时真是日狗了。dfs一发,记录一条边的一点一侧的点数num[i],这条边的另一点的相反侧的点数就是n-num[i],所以这个边被经过的次数就是num[i] ×(n-num[i]),再乘上这条边的长度len,就是对总的距离和的贡献。最后除以(n×(n-1)/2)就是最后的期望。特别要注意int 和long long 的转换,这里我因为吗,long long s=(long long)n*(n-1)/2;没注意,WA了很久,有时还莫名的TLE。最后改了这里才AC。


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<algorithm>
#include<string>
#include<queue>
#include<cmath>
#include<stack>

using namespace std;

const int maxn=100010;//最大点数
const int maxm=1000010;//最大边数
int F[maxn];//并查集,对应父节点
struct Edge{
    int u,v,w;
}edge[maxm];

struct Dis{
    int v,w;
};
vector<Dis> D[maxn];//最小生成树的邻接表
double dp[maxn];
int sum[maxn];//一侧的点数
int tol;

void addedge(int u,int v,int w){
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol++].w=w;
}

bool cmp(Edge a,Edge b){
    return a.w<b.w;
}

int findx(int x){
    return F[x]==-1?x:F[x]=findx(F[x]);
}

long long Kruskal(int n){
    memset(F,-1,sizeof(F));
    sort(edge,edge+tol,cmp);
    int cnt=0;
    long long ans=0;
    for(int i=0;i<tol;i++){
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int t1=findx(u);
        int t2=findx(v);
        if(t1!=t2){
            ans+=(long long)w;//虽然不知道需不需要,但是还是写了转化,确保万无一失
            D[v].push_back(Dis{u,w});
            D[u].push_back(Dis{v,w});
            F[t1]=t2;
            cnt++;
        }
        if(cnt==n-1)break;
    }
    if(cnt<n-1)return -1;//不连通
    else return ans;
}

void dfs(int n,int root,int father){
    sum[root]=1;
    for(int i=0;i<(int)D[root].size();i++){
        int son=D[root][i].v;
        int len=D[root][i].w;
        if(son==father)continue;
        dfs(n,son,root);
        sum[root]+=sum[son];
        dp[root]+=dp[son]+((double)sum[son]*(n-sum[son]))*len;
    }
}

void init(int n){
    memset(sum,0,sizeof(sum));
    memset(dp,0,sizeof(dp));
    memset(edge,0,sizeof(edge));
    for(int i=1;i<=n;i++){
        D[i].clear();
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        tol=0;
        init(n);
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
        }
        //这块地方类型转换需要特别注意
        long long ans=Kruskal(n);
        dfs(n,1,-1);
        long long s=(long long)n*(n-1)/2;
        printf("%I64d %.2f\n",ans,dp[1]/(double)s);
    }
    return 0;
}

Author : tak_fate@Acpp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值