zoukankan      html  css  js  c++  java
  • 树形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;
    }
    



  • 相关阅读:
    bzoj 1030 [JSOI2007]文本生成器
    Swift 学习笔记 (闭包)
    Swift 学习笔记 (函数)
    HTML 学习笔记 JQueryUI(Interactions,Widgets)
    HTML 学习笔记 JQuery(表单,表格 操作)
    HTML 学习笔记 JQuery(animation)
    HTML 学习笔记 JQuery(盒子操作)
    HTML 学习笔记 JQuery(事件)
    HTML 学习笔记 JQuery(DOM 操作3)
    HTML 学习笔记 JQuery(DOM 操作2)
  • 原文地址:https://www.cnblogs.com/james1207/p/3341575.html
Copyright © 2011-2022 走看看