• hdu4126Genghis Khan the ConquerorGenghis Khan the Conqueror(MST+树形DP)


    题目请戳这里

    题目大意:给n个点,m条边,每条边权值c,现在要使这n个点连通。现在已知某条边要发生突变,再给q个三元组,每个三元组(a,b,c),(a,b)表示图中可能发生突变的边,该边一定是图中的边。c表示该边新的权值,c只可能比原来的权值大。给的q条边发生突变的概率是一样的。求突变后连通n个点最小代价期望值。

    题目分析:如果没有那条突变的边,就是求一个mst。但是因为有一条边要突变,每条边突变的概率相同,都为1/q。所以要枚举所有的q条边,求出该边突变后最小生成树代价。q条边分2类:一类是生成树中的边,第二类不是生成树中的边。

    第二类边很好处理,既然本身不是生成树中的边。那么这条边突变后权值变大,现在要使权值最小,这条边不选就是了,所以总代价依然是原图的mst。

    第一类边发生突变的话,那么去掉这条边后原来的mst就被分成了2个子树,所以我们要找到解决办法就是寻找这2个子树之间的次短边(最小边是去掉的那条生成树中的边)。比较一下,如果这条次短边的权值小于这条突变的生成树上的边,那么就要换掉这条边,否则保留。

    现在的关键是求去掉某条边生成树边后,2个子树的最小距离。很容易想到一个O(n^3)的算法:枚举每条生成树边(u,v),分别从u和v开始沿着生成树边遍历,求出两两点间最小值。可是复杂度未免太高。借助dp的思想,可以将这个过程复杂度降一个n。

    考虑次过程的这样一个性质:求某个点i到以点j为根的树的最短距离=min(i到j所有子树的最短距离,i到j的最短距离),那么可以枚举起点i,从i点开始沿着mst的边dfs,每经过一条边,那么这条边可以将n个点分成2部分,一部分含i,另一部分不含i(废话),利用dfs的性质,对于从i出发沿着生成树的边遍历到的每个点,当要离开这个点的时候,保证其所有的子树都已经遍历完毕,那么i到j的子树的最短距离就确定了,那么以到达j的边为割边,i到j这颗子树的最短距离就有了。每一次从i点的dfs表示的是枚举割边后包含i的子树和不包含i的子树之间的最小距离。枚举每个点,就能得到对于所有生成树的边,dp[u][v]表示以(u,v)为割边的两颗子树的最短距离。

    好NB的DP!

    汉语组织的可能不是很好,具体画图吧,好容易懂的。

    详情请见代码:

    #include <iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 3005;
    const int M = 1000005;
    const int inf = 0x3f3f3f3f;
    int dp[N][N],dis[N][N],cost[N][N],lowcost[N],pre[N];
    bool used[N][N],flag[N];
    int head[N],num;
    double ans;
    int n,m,q,mst;
    struct node
    {
        int to,next;
    }e_mst[M];
    void build(int s,int e)
    {
        e_mst[num].to = e;
        e_mst[num].next = head[s];
        head[s] = num ++;
    }
    void prim()
    {
        int i,j;
        mst = 0;
        memset(flag,false,sizeof(flag));
        for(i = 0;i < n;i ++)
        {
            lowcost[i] = dis[0][i];
            pre[i] = 0;
        }
        flag[0] = true;
        for(i = 1;i < n;i ++)
        {
            int minn = inf;
            int v;
            for(j = 0;j < n;j ++)
            {
                if(lowcost[j] < minn && flag[j] == false)
                {
                    minn = lowcost[j];
                    v = j;
                }
            }
            mst += minn;
            used[pre[v]][v] = used[v][pre[v]] = true;
            build(pre[v],v);
            build(v,pre[v]);
            flag[v] = true;
            for(j = 0;j < n;j ++)
            {
                if(flag[j] == false && lowcost[j] > dis[v][j])
                {
                    lowcost[j] = dis[v][j];
                    pre[j] = v;
                }
            }
        }
    }
    int dfs(int cur,int u,int fa)//用cur更新cur点所在的子树和另外子树的最短距离
    {
        int ret = inf;
        for(int i = head[u];~i;i = e_mst[i].next)//沿着生成树的边遍历
        {
            if(e_mst[i].to == fa)
                continue;
            int tmp = dfs(cur,e_mst[i].to,u);//用cur更新的以当前边(u,e_mst[i].to)为割边的两个子树最短距离
            ret = min(tmp,ret);//以(fa,u)为割边的2个子树的最短距离
            dp[u][e_mst[i].to] = dp[e_mst[i].to][u] = min(dp[u][e_mst[i].to],tmp);
        }
        if(fa != cur)//生成树边不更新
            ret = min(ret,dis[cur][u]);
        return ret;
    }
    void solve()
    {
        int i,j;
        int a,b,c;
        int sum = 0;
        memset(head,-1,sizeof(head));
        num = 0;
        prim();
        for(i = 0;i < n;i ++)
            dfs(i,i,-1);
        scanf("%d",&q);
        for(i = 0;i < q;i ++)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(used[a][b])
            {
                if(c < dp[a][b])
                    sum += (mst - dis[a][b] + c);
                else
                    sum += (mst - dis[a][b] + dp[a][b]);
            }
            else
                sum += mst;
        }
        ans = (double)sum/(double)q;
        printf("%.4lf
    ",ans);
    }
    int main()
    {
        int i,j,a,b,c;
        while(scanf("%d%d",&n,&m),(m + n))
        {
            for(i = 0;i < n;i ++)
                for(j = 0;j < n;j ++)
                {
                    dp[i][j] = inf;
                    used[i][j] = false;
                    if(i == j)
                        dis[i][j] = 0;
                    else
                        dis[i][j] = inf;
                }
            for(i = 0;i < m;i ++)
            {
                scanf("%d%d%d",&a,&b,&c);
                dis[a][b] = dis[b][a] = c;
            }
            solve();
        }
        return 0;
    }
    //921MS	80028K
    


  • 相关阅读:
    Java的JXL操作xls形式
    UILabel iOS添加文本控件
    并行随机梯度下降
    ArcGIS For Flex给定两个
    如何判断一个网址是由哪种语言写的
    ArrayList线程不安全?
    java总结,错误集
    centos安装Chromium
    读书笔记:《从一到无穷大》
    读书笔记:《数学之美》
  • 原文地址:https://www.cnblogs.com/pangblog/p/3341828.html
Copyright © 2020-2023  润新知