ZOJ 3329 One Person Game (经典概率dp+有环方程求解)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3329
题意:
现在有三个骰子,分别有k1,k2和k3面,面上的点就是1~ki。每次扔骰子,如果这三个骰子的值分别对应为a,b,c,那么将值初始化为0,否则就将三个骰子的点值和相加。求大于等于n的扔骰子次数期望。
思路:
这道题目主要在于推公式,看着别人的题解想了好久。
先设$E(i)$为此时和为i时还需要的期望,易得(1):$E(i)=\sum (E(i+k)P(k))+E(0)P(0)+1$(这里$P(k)$为点值和为k的概率,$P(0)$就是对应a,b,c的概率)。
现在我们要求解的是$E(0)$,而且发现每个式子中都会包含$E(0)$。
当遇到这样的情况时我们可以先假设一下,假设(2):$E(i)=A(i)E(0)+B(i)$,那么(3):$E(i+k)=A(i+k)E(0)+B(i+k)$。
将(3)式带入(1)式,得(4):$E(i)=(\sum (A(i+k)P(k))+P(0))*E(0)+\sum (B(i+k)P(k))+1$。
将(2)式和(4)式作比较,可得:
(5):$A(i)=\sum (A(i+k)P(k))+P(0)$
(6):$B(i)=\sum (B(i+k)P(k))+1$
而由(2)式我们又可以推出$E(0)=\frac{B(0)}{1-A(0)}$。
所以现在要的就是A(0)和B(0)的值,这个很简单,由(5)、(6)递推得到即可。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<vector> 6 #include<stack> 7 #include<queue> 8 #include<cmath> 9 #include<map> 10 #include<set> 11 using namespace std; 12 typedef long long ll; 13 const int INF = 0x3f3f3f3f; 14 const int maxn=500+5; 15 16 int n,k1,k2,k3,a,b,c; 17 double p0; 18 double A[maxn],B[maxn],p[maxn]; 19 20 int main() 21 { 22 //freopen("in.txt","r",stdin); 23 int T; 24 scanf("%d",&T); 25 while(T--) 26 { 27 cin>>n>>k1>>k2>>k3>>a>>b>>c; 28 memset(p,0,sizeof(p)); 29 p0=1.0/k1/k2/k3; 30 for(int i=1;i<=k1;i++) 31 for(int j=1;j<=k2;j++) 32 for(int t=1;t<=k3;t++) 33 { 34 if(i==a && j==b && t==c) continue; 35 p[i+j+t]+=p0; 36 } 37 for(int i=n;i>=0;i--) 38 { 39 A[i]=p0,B[i]=1; 40 for(int t=3;t<=k1+k2+k3;t++) 41 { 42 if(i+t>n) continue; 43 A[i]+=A[i+t]*p[t]; 44 B[i]+=B[i+t]*p[t]; 45 } 46 } 47 printf("%.16f\n",B[0]/(1-A[0])); 48 } 49 return 0; 50 }