期望dp小结

前言:期望dp状态的定义是较为显然的,但对于状态的转移往往需要一些公式的推导。关键的几点是状态之间的互通性,和状态转移的花费,以及转移的概率


解决期望dp的几个技巧如下:

一.利用期望的线性性质:

E[X+Y]=E[X]+E[Y]
我们所求的期望可以化为多个步骤的期望累和

相关题目:J,L

二.采用逆序的方式:

在目标确定的情况下,可以得知在目标到达目标的期望值为0,然后根据期望的线性性质怎么又是线性性质 得到状态转移方程:
dp[i]+=(dp[j]+cost)*p cost是转移的花费 p是概率 有时要除以(1-转移到自己的概率,但这是又不能单纯地+cost)所以要结合具体情况考虑

相关题目:A,C,D,E

三.利用期望的定义求解:

E[A]=a1*p1+a2*p2+…+an*pn;
a1,a2…an为各种方案(情况)的花费(权值),p1,p2…pn为对应方案(情况)的发生概率 所以在最终方案确定且较少时,只用计算出发生的概率,在最后再乘上权值求累和就好了
相关题目:G,J


实际上,更多的题目需要自己推出公式,并按照推出的规律进行一些状态转移
像这种题,难度就会略高一些:
相关题目:B,H,I


题目选讲:
B:参见Komachi dalao博客
G:
看到 220 就应该知道可以用状压,
但如果单纯地算每个数被计算出的概率,那么复杂度为 n2nT ,稳定吃Tle
题目的另外一个特别之处在于:只有|,&,^三个运算符,他们在运算时是不会发生进位的,所以可以直接记录每一位数出现0或1的概率,然后在最后直接概率乘上权值就好了
参见代码:

#include
double dp[205][25][2],P[205];
int A[205];
int B[205][25];
char op[205][3];
int main(){
    int n,cnt=0;
    while(~scanf("%d",&n)){
        cnt++;
        for(int i=1;i<=n+1;i++)
            scanf("%d",&A[i]);
        for(int i=1;i<=n;i++)scanf("%s",op[i]);
        for(int i=1;i<=n;i++)scanf("%lf",&P[i]);

        for(int i=0;i<=20;i++)for(int j=0;j<=n;j++)for(int k=0;k<2;k++)dp[j][i][k]=0;
        for(int k=1;k<=n+1;k++)
            for(int i=0;i<=20;i++)
                if(A[k]&(1<1;
                else B[k][i]=0;

        for(int i=0;i<=20;i++)
            if(B[1][i])dp[0][i][1]=1;
            else dp[0][i][0]=1;

        for(int i=1;i<=n;i++){
            for(int j=0;j<=20;j++){
                double p=1-P[i];
                if(op[i][0]=='&'){
                    if(B[i+1][j]==0)dp[i][j][0]+=(dp[i-1][j][0]+dp[i-1][j][1])*p;
                    else {
                        dp[i][j][1]+=dp[i-1][j][1]*p;
                        dp[i][j][0]+=dp[i-1][j][0]*p;
                    }
                }
                else if(op[i][0]=='|'){
                    if(B[i+1][j]==0){
                        dp[i][j][0]+=dp[i-1][j][0]*p;
                        dp[i][j][1]+=dp[i-1][j][1]*p;
                    }   
                    else dp[i][j][1]+=(dp[i-1][j][1]+dp[i-1][j][0])*p;
                }
                else if(op[i][0]=='^'){
                    if(B[i+1][j]==0){
                        dp[i][j][0]+=dp[i-1][j][0]*p;
                        dp[i][j][1]+=dp[i-1][j][1]*p;
                    }
                    else {
                        dp[i][j][0]+=dp[i-1][j][1]*p;
                        dp[i][j][1]+=dp[i-1][j][0]*p;
                    }
                }

                dp[i][j][1]+=dp[i-1][j][1]*P[i];
                dp[i][j][0]+=dp[i-1][j][0]*P[i];
            }
        }

        double ans=0;
        for(int j=0;j<=20;j++)
            ans+=dp[n][j][1]*(1<printf("Case %d:\n%.6f\n",cnt,ans);
    }
    return 0;
}

J:
可以知道,对于m道题的每n道题,会对应不同的n个人
那么可以直接对这n个人对应n道题进行状压dp,复杂度 Tm2n
根据期望的线性性质,我们可以把每个阶段的期望值都加起来,然后就可以得到答案

#include
double dp[15][1200];
double P[15][1005];
template <class _> _ max(_ x,_ y){return x>y?x:y;}
template <class _> _ min(_ x,_ y){return xint main(){
    int T,t=0;
    scanf("%d",&T);
    while(T--){
        int n,m;
        scanf("%d %d",&n,&m);
        for(int i=0;ifor(int j=1;j<=m;j++)
                scanf("%lf",&P[i][j]);

        double ans=0;
        int cnt=(m-1)/n+1,tta=(1<1;
        for(int k=0;kint len=min(m,k*n+n);
            len-=k*n;
            for(int i=0;i<=len;i++)for(int j=0;j<=tta;j++)dp[i][j]=-1;
            dp[0][0]=0;
            for(int i=1;i<=len;i++){//k*n+i
                for(int j=0;j<=tta;j++){
                    if(dp[i-1][j]==-1)continue;
                    for(int h=0;hif((j&(1<continue;
                        dp[i][j|(1<1<1][j]+P[h][k*n+i]);
                    }
                }
            }
            double sum=0;
            for(int i=0;i<=tta;i++)
                if(dp[len][i]>sum)sum=dp[len][i];

            ans+=sum;
        }
        printf("Case #%d: %.5f\n",++t,ans);
    }
    return 0;
}

L:
仍是根据期望的线性性质,求被覆盖点的期望,可以化简为求所有点被覆盖的期望的累和。然后利用容斥,算出点不被覆盖的概率p,然后 1pk 即为该点被覆盖的概率,也是该点的期望值(由于贡献为1)

#include
int main(){
    int T,t=0;
    scanf("%d",&T);
    while(T--){
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        long long tmp=1ll*n*n*m*m;
        double ans=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                long long s=0;
                s+=1ll*n*n*(j-1)*(j-1);
                s+=1ll*m*m*(i-1)*(i-1);
                s+=1ll*n*n*(m-j)*(m-j);
                s+=1ll*m*m*(n-i)*(n-i);
                s-=1ll*(i-1)*(i-1)*(j-1)*(j-1);
                s-=1ll*(i-1)*(i-1)*(m-j)*(m-j);
                s-=1ll*(n-i)*(n-i)*(m-j)*(m-j);
                s-=1ll*(n-i)*(n-i)*(j-1)*(j-1); 
                double p=1.0*s/tmp;
                double d=1;
                for(int h=1;h<=k;h++)d=d*p;
                ans+=1-d;
            }
        }
        printf("Case #%d: %.f\n",++t,ans);
    }
    return 0;
}

你可能感兴趣的:(日常总结)