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; }