zoukankan      html  css  js  c++  java
  • 【BZOJ3925】地震后的幻想乡(ZJOI2015)-概率期望+子集状压DP

    测试地址:地震后的幻想乡
    做法:本题需要用到概率期望+子集状压DP。
    题目要求最小生成树最大边的期望,我们知道这个值等于最大边的期望排名(从小到大)/(m+1),因为提示里说了,m[0,1]内的随机变量的第k小值的期望为km+1
    那么令L为最大边的排名,则有:
    ans=E[L]m+1=1m+1x=1mxP(L=x)
    得到这个和式,我们变换它的求和顺序,可得:
    ans=x=1mP(Lx)
    而最大边x的概率,等于用排名前x1的边不能使图连通的概率,令这个概率为T(x1),我们要求:
    ans=x=0m1T(x)
    因为每种x条边的组合作为排名前x的边的概率是相等的,于是T(x)实际上就等于,用x条边使得图不能连通的方案数,除以所有x条边的组合数。于是问题变成了如何求用x条边使得图不能连通的方案数。
    于是我们有了一个定义状态的思路:令f(i,j)为用(连接点集i内部点的)j条边使得点集i不连通的方案数。我们发现只有这个状态不好转移,因为一般这种连通性状压DP的转移思路都是,通过枚举某个点所在的连通块,来做到不重不漏地枚举。于是我们有另一个状态定义:令g(i,j)为用(连接点集i内部点的)j条边使得点集i连通的方案数,那么令ki的一个包含某定点P的真子集,有以下状态转移方程:
    f(i,j)=kl=0edge(k)g(k,l)Cedge(ik)jl
    其中edge(S)表示连接点集S内部点的边的数量,那么上式的含义其实就是,枚举定点P所在的连通块,求剩下的点集不和该连通块连接的方案数。而我们发现fg的定义是互补的,所以有:
    g(i,j)=Cedge(i)jf(i,j)
    这样我们就可以同时转移fg了。那么令all为全集,答案就为:
    ans=1m+1x=0m1f(all,x)Cedge(all)x
    于是我们就解决了这一题,时间复杂度为O(3nn2)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    int n,m,a[60],b[60],edge[1210];
    double C[60][60]={0},f[1210][60],g[1210][60];
    
    void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&a[i],&b[i]);
    
        C[0][0]=1.0;
        for(int i=1;i<=m;i++)
        {
            C[i][0]=1.0;
            for(int j=1;j<=i;j++)
                C[i][j]=C[i-1][j-1]+C[i-1][j];
        }
    
        for(int i=1;i<(1<<n);i++)
        {
            edge[i]=0;
            for(int j=1;j<=m;j++)
                if ((i&(1<<(a[j]-1)))&&(i&(1<<(b[j]-1))))
                    edge[i]++;
        }
    }
    
    void work()
    {
        for(int i=1;i<(1<<n);i++)
        {
            for(int j=0;j<=edge[i];j++)
            {
                f[i][j]=0.0;
                int lowbit=i&(-i);
                for(int k=((i-1)&i);k;k=((k-1)&i))
                    if (k&lowbit)
                    {
                        for(int l=0;l<=edge[k]&&l<=j;l++)
                            f[i][j]+=g[k][l]*C[edge[i-k]][j-l];
                    }
                g[i][j]=C[edge[i]][j]-f[i][j];
            }
        }
        double ans=0.0;
        for(int i=0;i<m;i++)
            ans+=f[(1<<n)-1][i]/C[edge[(1<<n)-1]][i];
        ans/=(double)(m+1);
        printf("%.6lf",ans);
    }
    
    int main()
    {
        init();
        work();
    
        return 0;
    }
  • 相关阅读:
    Linux下C程序插入执行shell脚本
    #ifdef预编译相关用法
    LAMP开发之环境搭建(2014.12.7在ubuntu下)
    Qt在VS2010的安装与配置
    vs2010配备boost编程环境
    Ubuntu虚拟机与Window、Arm的通信
    大小端测试程序
    Ubuntu安装google Gtest
    设计模式之单例模式
    设计模式之原型模式
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793311.html
Copyright © 2011-2022 走看看