• 树形dp+MST-hdu-4126-Genghis Khan the Conqueror


    题目链接:

    http://acm.hdu.edu.cn/showproblem.php?pid=4126

    题目意思:

    给一图,n个点,m条边,每条边有个花费,给出q条可疑的边,每条边有新的花费,每条可疑的边出现的概率相同,求不能经过原来可疑边(可以经过可疑边新的花费构建的边),注意每次只出现一条可疑的边,n个点相互连通的最小花费的期望。

    解题思路:

    树形dp+MST。

    先用kruskal算法找到最小生成树,并求出总花费sum.

    再以枚举n个点,依次作为树根dfs,dp[i][j]表示<i,j>为最小生成树上的边,且去掉该边后,包括点i的连通块中的点集A到包括点j的连通块点集B的最小距离。

    对于根节点为ro,边为<i,j>的dp[i][j]=min(以j节点为根的子树到ro的最短距离,dp[i][j]).

    如下图所示:


    以右边点集为子树求出左边点集中每个点作为树根时到all的最小距离。其实对于上面那条边,只用枚举ro个点就行了,但是不好确定每条边的ro集,所以枚举n个点,作为ro,然后dfs,最每条边更新一次,时间复杂度为o(n^2)可以接受。

    代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<map>
    #include<set>
    #include<stack>
    #include<list>
    #include<queue>
    #include<ctime>
    #define eps 1e-6
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    #define ll __int64
    #define lson l,m,(rt<<1)
    #define rson m+1,r,(rt<<1)|1
    #pragma comment(linker, "/STACK:1024000000,1024000000")
    using namespace std;
    
    #define Maxn 3300
    struct Edge
    {
        int a,b,c;
    }edge[Maxn*Maxn]; //保存边的信息
    
    int dis[Maxn][Maxn]; //原始距离
    bool hav[Maxn][Maxn]; //是否为最小生成树上的边
    int fa[Maxn],dp[Maxn][Maxn];//dp[i][j]表示<i,j>为最小生成树上的边,且去掉该边后,包括点i的连通块中的点集A到包括点j的连通块点集B的最小距离。
    int n,m,cnt;
    ll sum;
    
    int find(int x) //并查集
    {
        int tmp=x;
        while(x!=fa[x])
            x=fa[x];
        while(fa[tmp]!=x)
        {
            int tt=fa[tmp];
            fa[tmp]=x;
            tmp=tt;
        }
        return x;
    }
    bool cmp(struct Edge a,struct Edge b)
    {
        return a.c<b.c;
    }
    struct EE //构建最小生成树
    {
        int v;
        struct EE * next;
    }ee[Maxn<<1],*head[Maxn<<1];
    
    void add(int a,int b)
    {
        ++cnt;
        ee[cnt].v=b;
        ee[cnt].next=head[a];
        head[a]=&ee[cnt];
    }
    
    void kruskal() //克鲁斯卡尔算法求最小生成树
    {
        sum=0;
        cnt=0;
        for(int i=1;i<=m;i++)
        {
            int a=find(edge[i].a),b=find(edge[i].b);
            if(a!=b)
            {
                fa[b]=edge[i].a;
                sum+=edge[i].c;
                hav[edge[i].a][edge[i].b]=hav[edge[i].b][edge[i].a]=true;
                add(edge[i].a,edge[i].b);  //建树
                add(edge[i].b,edge[i].a);
            }
        }
    }
    int dfs(int ro,int fa,int cur,int dep) //表示以cur作为当前子树根中所有子树节点到总根ro的最短距离
    {
        struct EE * p=head[cur];
        int mi=INF;
    
        if(dep!=1) //不为树根的儿子
            mi=dis[cur][ro];
        while(p)
        {
            int v=p->v;
            if(v!=fa)
            {
                int tt=dfs(ro,cur,v,dep+1);
                mi=min(mi,tt);
                dp[cur][v]=dp[v][cur]=min(dp[v][cur],tt);//更新当前边
            }
            p=p->next;
        }
        return mi;
    
    }
    
    int main()
    {
       // printf("%d
    ",INF);
        while(scanf("%d%d",&n,&m)&&n+m)
        {
            memset(dis,INF,sizeof(dis));
            for(int i=1;i<=m;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                edge[i].a=a,edge[i].b=b,edge[i].c=c;
                dis[a][b]=dis[b][a]=c;
            }
            sort(edge+1,edge+m+1,cmp);
            for(int i=0;i<n;i++)
                fa[i]=i;
            memset(hav,false,sizeof(hav));
            memset(head,NULL,sizeof(head));
            kruskal();
    
            memset(dp,INF,sizeof(dp));
            for(int i=0;i<n;i++)  //以每个点最为树根,对每条边更新n次
                dfs(i,i,i,0);
    
            ll ans=0;
            int q;
            scanf("%d",&q);
            for(int i=1;i<=q;i++)
            {
                int a,b,c;
                scanf("%d%d%d",&a,&b,&c);
                if(hav[a][b]) //是最小生成树上的边
                {
                    int tt=min(dp[a][b],c); //要么用新边,要么用不是最小生成树上的边
                    ans=ans+sum-dis[a][b]+tt;
                }
                else //不是最小生成树上的边,直接用最小生成树
                    ans=ans+sum;
               // printf("*%lf
    ",ans);
            }
            printf("%.4f
    ",ans*1.0/q);
        }
       return 0;
    }
    



  • 相关阅读:
    31天重构学习笔记21. 合并继承
    31天重构学习笔记15. 移除重复内容
    31天重构学习笔记22. 分解方法
    31天重构学习笔记18. 使用条件判断代替异常
    31天重构学习笔记19. 提取工厂类
    31天重构学习笔记24. 分解复杂判断
    31天重构学习笔记17. 提取父类
    大型项目的发布部署:第一章:发布部署流程
    HDU 2036 改革春风吹满地 数学题
    HDU 2051 Bitset
  • 原文地址:https://www.cnblogs.com/james1207/p/3341575.html
Copyright © 2020-2023  润新知