ZOJ 3329 概率dp求期望

传送门

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

题意:给定三个骰子,分别有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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值