[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
",B[0]/(1-A[0]));
33     }
34     return 0;
35 }
View Code
原文地址:https://www.cnblogs.com/Ngshily/p/5519556.html