hdu 5723 Abandoned country 深搜回溯

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/82969893

题目链接:hdu 5723

参考博客:https://www.cnblogs.com/aiguona/p/7214707.html

题意:给出n个顶点,m条边,让你建一颗最小生成树,问:最小生成树的值是多少,任意两点的期望长度是多少?

题解:

      任意两点的期望是(权值)*(这条路被走过的次数)的总和   除以  总共的路径数

  比如从A到B距离为2,从B到C距离为3。那么从A到B走过2一次,从A到C走过2一次,走过3一次,从B到C走过3一次。

  所以期望为(2*2+3*2)/ 3 = 3.33。(也可以看成有三条路径,1,A->B , B->C , A->C 其实B->A也算一种的,但我们可以不用考虑,因为求得是期望值,所以我们就按顺着的方向来算路径)。

  总共的路径数 n*(n-1)/2 ,举个例子给你,有4个顶点,那么就有3个值,那么首先有3条路径,分别为 A->B B->C C->D (经过两个顶点),

然后有2条路径,分别为 A->C B->D(经过三个顶点) , 最后有1条路径,为 A->D(经过4个顶点),很容易可以看出是个等差数列和(每次少一条路径)。

     

    这里不能求最短路,超时。

  应该用深搜回溯,找出这条路被走过过少次。这样就可以求出期望了。

  这里由于数据量比较大,不能用prim邻接矩阵求最小生成树(vector应该可以),可以用Kruskal求最小生成树。

  坑点:注意最后求期望的时候总共的路径数量n*(n-1)/2数据量比较大,应该用long long或者double存储。

///代码参考博客:https://www.cnblogs.com/aiguona/p/7214707.html
#include<cstdio>
#include<algorithm>
#include<vector>
#include<iostream>
#include<string.h>
using namespace std;
vector<pair<int,int> >  v[100010];

struct Edge
{
    int f,t,q;
};

int m,n;///n为村庄数,m为街道数
Edge s[1000010];///存储图
long long  ans;///存最后的每条路的总和
int pre[100010];///并查集的祖先数组
int vis[100010];///标记数组

bool cmp(Edge a,Edge b )///排序函数
{
    return a.q<b.q;
}

int Find(int x)///找祖先
{
    if(x!=pre[x])
    {
        pre[x]=Find(pre[x]);
    }
    return pre[x];
}

void Merge(int x,int y)///查是否相等
{
    int fx=Find(x);
    int fy=Find(y);
    if(fx!=fy)
        pre[fx]=fy;
}

long long dfs(int x) ///dfs递归搜索
{
    vis[x]=1;
    long long now=0,all=1;///now记录当前节点直接连接的节点数量  all记录此节点经过搜索后所有的与此节点连接的节点数
    int h=v[x].size();
    for(int i=0; i<h; i++)
    {
        int b=v[x][i].first;
        if(!vis[b])
        {
            now=dfs(b);

            all+=now;
//            printf("b=%d,all=%lld,now=%lld\n",b,all,now);
            ///路径数为 now*(n-now),解释下:与x节点连接的后续节点数为now个,那么与x连接的前置节点就有(n-now)个,
            ///所有的前置节点都可以和后续节点形成一条顺着的路径
            ///故为 now*(n-now)条路径
            ans+=now*(n-now)*v[x][i].second;///ans记录的是权值*经过的次数
        }
    }
    return all;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        memset(vis,0,sizeof(vis));
        scanf("%d%d",&n,&m);
        if(m==0||n==0)
            {printf("0 0.00");continue;}

        for(int i=0;i<=n;i++)
            v[i].clear();
        for(int i=0; i<=n; i++)///并查集的祖先节点的初始化
        {
            pre[i]=i;
        }
        for(int j=0; j<m; j++)///输入
        {
            scanf("%d%d%d",&s[j].f,&s[j].t,&s[j].q);
        }
        sort(s,s+m,cmp);///排序
        long long sum=0;///sum用来记录最小生成树的长度
        for(int j=0; j<m; j++)
        {
            int fx=Find(s[j].f);
            int fy=Find(s[j].t);
            if(fx!=fy)  ///如果祖先不相等,那么加入到最小生成树中
            {
                sum=sum+s[j].q;
                Merge(fx,fy);
                ///加入到动态数组中准备做期望
                v[s[j].f].push_back(make_pair(s[j].t,s[j].q));
                v[s[j].t].push_back(make_pair(s[j].f,s[j]. q));
            }
        }
        dfs(1);///深搜回溯计算ans的值
        double y=1.0*n*(n-1)/2;
        printf("%lld %.2lf\n",sum,(double)ans/y);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/82969893
今日推荐