ZOJ 3329 概率dp求期望

来源:互联网 发布:java 泛型 类型转换 编辑:程序博客网 时间:2024/06/06 07:33

传送门

先记录一下这题怎么做……等做多了再归纳总结一下,这个先留个坑……

题意:给定三个骰子,分别有k1、k2、k3三个面,1<K1,K2,K3<=6,每次计数从0开始,当掷得的三个骰子各为a、b、c时,计数归零,否则计数加上三个骰子的数之和,计数大于等于n时停止,求需要掷的次数的期望

思路:设dp[i]为当前计数为i到目标所需要的次数的期望,那么可以很容易得到:

dp[i]=Σdp[i+k]*pk+dp[0]*p0+1                ————————————(1)

pk为掷得的点数和为k的概率,p0为正好掷得a、b、c的概率,实际就是1/(k1*k2*k3),我们知道dp[n]、dp[n+1]...都等于0,想逆向求得dp[i],可是有dp[0]这个未知量,但dp[0]又正好是我们需要求的量,可以说dp[i]都与dp[0]有关,我们可以设:

dp[i]=A[i]dp[0]+B[i]                                 ————————————(2),那么带入(1)式,可以得到:

dp[i]=Σ(A[i+k]*dp[0]*pk+B[i+k]*pk)+dp[0]*p0+1

       =(ΣA[i+k]*pk+p0)*dp[0]+ΣB[i+k]*pk+1———————————(3)

(2)式和(3)式对应相等,那么:

A[i]=ΣA[i+k]*pk+p0

B[i]=ΣB[i+k]*pk+1

这两个式子都是不带未知量的递推公式,我们可以事先预处理得到pk,然后逆推可以依次得到A[i]、B[i],最终得到A[0]、B[0],那么dp[0]=B[0]/(1-A[0])

我在想这是不是一个通用的方法目的是把未知量dp[0]消去,然后就能求递推式了,现在还不好下结论,毕竟还是做的题少……

完整代码:

#include<cstring>#include<cmath>#include<cstdio>#include<iostream>#include<algorithm>using namespace std;typedef long long ll;const int N=605;double A[N],B[N];double p[100];int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n,k1,k2,k3,a,b,c;        scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);        double tmp=1.0/(k1*k2*k3);        memset(p,0,sizeof(p));        for(int i=1;i<=k1;i++)            for(int j=1;j<=k2;j++)                for(int k=1;k<=k3;k++)                    if(!(i==a&&j==b&&k==c))                        p[i+j+k]+=tmp;        memset(A,0,sizeof(A));        memset(B,0,sizeof(B));        for(int i=n;i>=0;i--)        {            A[i]=tmp;            B[i]=1;            for(int j=1;j<=k1+k2+k3;j++)            {                A[i]+=(A[i+j]*p[j]);                B[i]+=(B[i+j]*p[j]);            }        }        printf("%.16lf\n",B[0]/(1-A[0]));    }    return 0;}