[ZOJ 3329] One Person Game
有三个分别有K1,K2,K3个面的骰子,每个面上的值为[1,Ki]
设所投出的序列为{k1,k2,k3}
当投出{a,b,c}时得分清零,否则得分增加k1+k2+k3
求使得得分大于等于n的期望步数
设dp[i]表示当前得分为i,达成目标所需的期望步数
dp[i]=dp[i+k]*p[k]+dp[0]*p0+1
其中p[i]表示使得分增加i的概率
由于dp[i]与dp[0]有关,然而dp[0]未知,所以我们把dp[0]提出来
则dp[i]=A[i]*dp[0]+B[i]
带入原式得
dp[i]=(A[i+k]*dp[0]+B[i+k])*p[k]+dp[0]*p0+1
=A[i+k]*dp[0]*p[k]+B[i+k]*p[k]+dp[0]*p0+1
=dp[0]*(A[i+k]*p[k]+p0)+(B[i+k]*p[k]+1)
所以A[i]=A[i+k]*p[k]+p0,B[i]=B[i+k]*p[k]+1
边界:对于所有i>n,A[i]=B[i]=0
ans=dp[0]=B[0]/(1-A[0])
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define maxn 600 4 int T,n,K1,K2,K3,a,b,c; 5 double p0,A[maxn],B[maxn],p[maxn]; 6 void PP(){ 7 memset(p,0,sizeof(p)); 8 p0=1.0/(K1*K2*K3); 9 for(int i=1;i<=K1;i++) 10 for(int j=1;j<=K2;j++) 11 for(int k=1;k<=K3;k++){ 12 if(i==a&&j==b&&k==c)continue; 13 p[i+j+k]+=p0; 14 } 15 } 16 int main(){ 17 scanf("%d",&T); 18 while(T--){ 19 scanf("%d",&n); 20 scanf("%d%d%d",&K1,&K2,&K3); 21 scanf("%d%d%d",&a,&b,&c); 22 PP(); 23 memset(A,0,sizeof(A)); 24 memset(B,0,sizeof(B)); 25 for(int i=n;i>=0;i--){ 26 A[i]=p0,B[i]=1; 27 for(int j=3;j<=K1+K2+K3;j++){ 28 A[i]+=p[j]*A[i+j]; 29 B[i]+=p[j]*B[i+j]; 30 } 31 } 32 printf("%.15lf\n",B[0]/(1-A[0])); 33 } 34 return 0; 35 }