ZOJ-3329 One Person Game (有环期望问题)

题目大意:有3个骰子,各有k1,k2,k3个面,面值为1~ki。还有一个计数器,初始值为0,统计所有的面值和。每次同时置这三个骰子,如果第一个骰子的朝上的值为a、第二个值为b、第三个值为c,那么将计数器置为零。直到计数器的值大于n时结束,求次数的期望值。

题目分析:这道题的状态转移方程不难写。定义状态dp(i)表示计数器值为 i 时还可以置几次,另外定义pk表示一次置出的3个骰子之和为k的概率,p0表示置出a、b、c的概率。则状态转移方程为:dp(i)= ∑pk*dp(i+k)+p0*dp(0)+1  <1>。到这儿就不知道怎么干了,查了下题解:定义dp(i)=A(i)*dp(0)+B(i),并将其代入<1>,得到A(i)= p0+∑pk*A(i+k),B(i)=1+ ∑pk*B(i+k)。那么只需要通过递推得到A(0)和B(0),就可以解得dp(0)=(1-A(0))/B(0)。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;

int n,a,b,c;
int k1,k2,k3;
double A[600],B[600];
double p[20];

void init()
{
    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]+=1.0/k1/k2/k3;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
        init();
        double p0=1.0/k1/k2/k3;
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        for(int i=n;i>=0;--i){
            A[i]=p0;
            B[i]=1;
            for(int j=1;j<=k1+k2+k3;++j){
                A[i]+=p[j]*A[i+j];
                B[i]+=p[j]*B[i+j];
            }
        }
        printf("%.16lf\n",B[0]/(1-A[0]));
    }
    return 0;
}

  

posted @ 2016-03-26 00:21  20143605  阅读(191)  评论(0编辑  收藏  举报