ACdream oj1113 概率dp求期望

来源:互联网 发布:电信5s4g网络怎么设置 编辑:程序博客网 时间:2024/06/18 11:07

传送门

题意:一个6个面的骰子,给定一个固定的数,问使掷得骰子的和正好等于这个数的次数的期望,值得注意的是,如果当前的数为x,掷得的数为y,x+y>n,那么这次掷得的结果作废

思路:用了一个超级笨的方法做的……

先计算了一下前几个数的期望,从1开始,想得到1只能某次掷到1才可以,那么:

dp[1]=1*1/6+2*5/6*1/6+3*(5/6)^2*1/6+......=6

再计算了一下2,想得到2的方式有两个:2+0、1+1,那么:

dp[2]=1*1/6+2*4/6*1/6+3*(4/6)^2*1/6+......+(dp[1]+1)*1/6+(dp[1]+2)*4/6*1/6+(dp[2]+3)*(4/6)^2*1/6+......=6

类似的思想依次计算了一下发现dp[1]——dp[6]都是6

然后再计算一下dp[7],7可以由1+6、2+5、3+4、4+3、5+2、6+1、7+0得到,其中7不会被骰子掷出,所以我们只考虑前六种,dp[7]=1/6*(dp[6]-1)+1/6*(dp[5]-1)+1/6*(dp[4]+1)+1/6*(dp[3]+1)+1/6*(dp[2]+1)+1/6*(dp[1]+1)=1/6*(dp[1]+dp[2]+dp[3]+dp[4]+dp[5]+dp[6]+6),

根据这个思想就可以发现dp[i]=1/6(dp[i-1]+dp[i-2]+dp[i-3]+dp[i-4]+dp[i-5]+dp[i-6]+6)

完整代码:

#include<cstring>#include<cmath>#include<cstdio>#include<iostream>#include<algorithm>using namespace std;typedef long long ll;const int N=1e5+5;double dp[N];int main(){    for(int i=1;i<=6;i++)        dp[i]=6;    for(int i=7;i<N;i++)    {        dp[i]=1/(double)6*(dp[i-1]+dp[i-2]+dp[i-3]+dp[i-4]+dp[i-5]+dp[i-6]+6);    }    int t;    scanf("%d",&t);    while(t--)    {        int n;        scanf("%d",&n);        printf("%.2f\n",dp[n]);    }    return 0;}
感觉到这个做法有些笨,又从网上看了众dalao做法,概率dp求期望是否是一个固定的套路?现在做的题目太少,也不好下结论……

设dp[i]为从i扔到n所需次数的期望,那么dp[n]=0,这样我们就可以逆着推dp[0]了,如果设cnt为掷得的数y满足i+y>n的的个数,那么就能得到这么个式子:

dp[i]=cnt/6*dp[i]+1/6*dp[i+1]+1/6*dp[i+2]+...+1

dp[i]=(1/6*dp[i+1]+1/6*dp[i+2]+...+1)/(6-cnt)*6

完整代码:

#include<cstring>#include<cmath>#include<cstdio>#include<iostream>#include<algorithm>using namespace std;typedef long long ll;const int N=1e5+5;double dp[N];int main(){    int t;    scanf("%d",&t);    while(t--)    {        memset(dp,0,sizeof(dp));        int n;        scanf("%d",&n);        for(int i=n-1;i>=0;i--)        {            int cnt=0;            double tmp=0;            for(int j=1;j<=6;j++)            {                if(i+j>n) cnt++;                else tmp+=(dp[i+j]/6);            }            dp[i]=(tmp+1)/(6-cnt)*6;        }        printf("%.2f\n",dp[0]);    }    return 0;}


原创粉丝点击