zoukankan      html  css  js  c++  java
  • 【BZOJ3925】[ZJOI2015] 地震后的幻想乡(状压期望DP)

    点此看题面

    大致题意:(n)个点和(m)条边,每条边的权值是一个(0sim1)的随机实数,要你用(n-1)条边将图联通,问这(n-1)条边中边权最大值的期望最小值。

    提示

    这题应该是一道比较难的(DP)题吧。

    首先,我们需要注意到提示中的一句话:

    (Hint)

    对于(n)([0,1])之间的随机变量(x_1,x_2,...,x_n),第(k)小的那个的期望值是(frac k{n+1})

    其实,这就很明显在提示我们,只要求出这(n-1)条边中权值最大边的期望最小排名,我们就能轻松求出它的期望最小值

    于是我们就能形成一个比较基础的思路了。

    基础思路

    首先,应该不难想到,可以用(P_i)来表示权值最大边排名为(i)的概率,而答案就是(sum_{i=1}^mi·P_i)(这应该还是比较显然的)。

    但问题在于这样很难进行状态转移。

    我们可以将原式进行进一步的转化,则不难发现,答案其实就相当于(sum_{i=1}^msum_{j=i}^mP(j))

    由于(sum_{i=1}^mP(i)=1),所以上面式子中的(sum_{j=i}^mP(j))其实又等价于(1-sum_{j=1}^{i-1}P(j))

    考虑(sum_{j=1}^{i-1}P(j))表示的是加入(i-1)条边能使原图联通的概率,所以(1-sum_{j=1}^{i-1}P(j))其实就相当于加入(i-1)条边不能使原图联通的概率

    不难发现,这比求原式要容易多了。

    转化后的问题

    整理一下上面的思路,就可以得到一个这样的问题:设(P'(i))为加入(i)条边后原图不连通的概率,求(sum_{i=0}^{m-1}P'(i))

    注意到(nle10)这极小的范围,我们首先想到的自然是乱搞状压(DP)

    状压(DP)

    考虑用(f_{i,t,0/1})来表示连接(i)条边后使点集(j)不连通/联通的方案数,注意,这里记录的是方案数而不是概率,这样可以防止出现精度误差(毕竟要保留(6)位小数)。

    我们可以用(Size_i)(line\_tot_i)分别表示点集(i)点与边的数量

    首先,我们要知道一个比较基础的知识,即在点集(i)中选择(t)条边的方案数应为(C_{line\_tot_i}^t),即(f_{i,t,0}+f_{i,t,1}=C_{line\_tot_i}^t)

    既然这样,只要求出(f_{i,t,0})(f_{i,t,1})中的一个,我们就可以轻松求出另一个的值了。

    比较显然(f_{i,t,0})相对而言要更容易求一些。

    状态转移

    我们来考虑如何进行状态转移。

    考虑在当前点集中任意选取一点(k)作为定点((k)取什么点无所谓,为了方便推荐选择(lowbit(i)),可以(O(1))求出),然后枚举含有点(k)的点集(j),进行转移。

    不难想到,如果要让点集(j)变成(i),自然需要加入(i)^(j)这个点集。

    我们可以在保证点集(j)联通的情况下,枚举(x,y(0le xle line\_tot_j,0le yle line\_tot_{i ext{^}j})),表示两个点集中的分别选择的边数。

    由于选(x)条边使点集(j)联通的方案数为(f_{j,x,1}),而(y)条边的选择方案数共有(C_{line\_tot_{i ext{^}j}}^y),所以可以得出转移方程:

    [f_{i,x+y,0}=sum f_{j,x,1}*C_{line\_tot_{i ext{^}j}}^y ]

    这样就可以不重不漏地统计到所有情况了。

    最后的答案

    根据一开始得出的结论,最后的答案就是:

    [frac{sum_{i=0}^{m-1}frac{f_{2^n-1,i,0}}{C_m^i}}{m+1} ]

    还有,千万注意精度啊。

    一些细节:关于(Size)(line\_tot)数组的求法

    关于(Size)(line\_tot)两个数组,其实有一种比较巧妙的求法。(会的大佬可以直接跳过这一部分)

    • (Size_i=Size_{i>>1}+(i&1))
      • 首先,由于((i>>1)<i),所以(Size_{i>>1})一定在求(Size_i)之前已经求解过。
      • 其次,由于((i>>1))(i)在二进制下只相差最后一位,所以只要比较最后一位(即(i&1))就可以求出结果。
    • (line\_tot_i=frac{sum Size_{Next_j&i}}2)
      • 注意,上面式子中的(j)表示点集(i)中的一个点,而(Next_j)表示与(j)有边相连的点的点集。
      • 首先,由于(Next_j&ile i),所以(Size_{Next_j&i})肯定已经求解过。
      • 其次,将与(j)相连的点与点集(i)中的点(&)一下,得到的结果就是点集(i)中与(j)相连的点的点集,它的(Size)就是点集(i)中与(j)有边相连的点的数量,即以点(j)为一个端点的边数。
      • 但这样会把每条边统计两次,因此最后除以(2)即可。

    代码

    #include<bits/stdc++.h>
    #define N 10
    #define M 50
    #define LL long long
    using namespace std;
    int n,m,Next[N+5],line_tot[1<<N+1],Size[1<<N+1];LL f[1<<N+1][M+5][2],C[M+5][M+5];
    class Class_FIO
    {
        private:
            #define Fsize 100000
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
            char ch,*A,*B,Fin[Fsize];
        public:
            Class_FIO() {A=B=Fin;}
            inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
    }F;
    inline void Init() {for(register int i=C[0][0]=1,j;i<=M;++i) for(C[i][0]=C[i][i]=j=1;j<i;++j) C[i][j]=C[i-1][j-1]+C[i-1][j];}//预处理组合数
    int main()
    {
        register int i,j,k,x,y,Full;register double ans=0.0;
        for(Init(),F.read(n),F.read(m),i=1;i<=m;++i) F.read(x),F.read(y),Next[x]|=(1<<y-1),Next[y]|=(1<<x-1);//用Next[i]存储与i有边相连的点的点集
        for(i=1,Full=(1<<n)-1;i<=Full;++i)//枚举每一个点集i
        {
            if((Size[i]=Size[i>>1]+(i&1))==1) {f[i][0][1]=1;continue;}//如果只有一个点,跳过
            for(j=1;j<=n;++j) if(i&(1<<j-1)) line_tot[i]+=Size[Next[j]&i];line_tot[i]>>=1;//统计边数
            for(k=i&-i,j=i&(i-1);j;j=i&(j-1)) if(j&k) for(x=0;x<=line_tot[j];++x) for(y=0;y<=line_tot[i^j];++y) f[i][x+y][0]+=f[j][x][1]*C[line_tot[i^j]][y];//状态转移
            for(j=0;j<=line_tot[i];++j) f[i][j][1]=C[line_tot[i]][j]-f[i][j][0];//根据f[i][j][0]得到f[i][j][1]的值
        }
        for(i=0;i<=m;++i) ans+=1.0*f[Full][i][0]/C[line_tot[Full]][i];ans/=m+1;//统计答案
        return printf("%.6lf",ans),0;
    }
    
  • 相关阅读:
    车载导航系统中GPS的定位
    《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录
    Mysql insert语句的优化
    Codeforces 475 D. CGCDSSQ
    提高代码编码的效率,习惯非常重要!
    ubuntu14.04 安装LNMP
    Unity3d数据加密
    第14章3节《MonkeyRunner源代码剖析》 HierarchyViewer实现原理-HierarchyViewer实例化
    Polyfill简介
    只在需要的时候 Polyfill 你的 JavaScript 代码
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3925.html
Copyright © 2011-2022 走看看