题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3329
题意:
给你面数分别为k1,k2,k3的三个骰子。
给定a,b,c三个整数。
三个骰子每扔一次,若骰子朝上的点数分别为a,b,c,则分数清零,否则当前分数+=骰子点数之和。
当分数 > n时游戏结束。
问你扔骰子次数的期望。
题意:
表示状态:
dp[i] = rest steps
(当前分数为i时,剩余步数的期望)
找出答案:
ans = dp[0]
刚开始分数为0。
如何转移:
由于此题中可以由高分数转移到低分数,所以转移存在环。
一般有环dp用高斯消元做。
但是,此题的所有环都跟dp[0]有关,也就是说所有的转移都能写成形如 dp[i] = a[i]*dp[0] + b[i] 的形式(分离系数)。
那么求出a[0]和b[0]就可以行了,答案为dp[0] = b[0] / (1-a[0])。
(1)dp[i] = sigma(dp[i+k]*p[k]) + dp[0]*p[0] + 1 (原转移方程,p[i]为扔出点数为i的概率,p[0]为扔出(a,b,c)的概率)
(2)dp[i] = a[i]*dp[0] + b[i] (假设的)
将(2)代入(1):
dp[i] = sigma( (a[i+k]*dp[0] + b[i+k]) * p[k] ) + dp[0]*p[0] + 1
dp[i] = sigma( a[i+k]*dp[0]*p[k] + b[i+k]*p[k] ) + dp[0]*p[0] + 1
dp[i] = ( sigma(a[i+k]*p[k]) + p[0] )*dp[0] + sigma(b[i+k]*p[k]) + 1
系数对应相等:
a[i] = sigma(a[i+k]*p[k]) + p[0]
b[i] = sigma(b[i+k]*p[k]) + 1
递推求出a[i] & b[i]即可,求的时候要保证i <= n(有意义)。
dp[0] = b[0] / (1-a[0])。
AC Code:
1 // state expression: 2 // dp[i] = rest steps 3 // i: present score 4 // 5 // find the answer: 6 // ans = dp[0] 7 // 8 // transferring: 9 // 1) dp[i] = sigma(dp[i+k]*p[k]) + dp[0]*p[0] + 1 10 // 2) dp[i] = a[i]*dp[0] + b[i] 11 // ***solve: 12 // dp[i] = sigma( (a[i+k]*dp[0] + b[i+k]) * p[k] ) + dp[0]*p[0] + 1 13 // dp[i] = sigma( a[i+k]*dp[0]*p[k] + b[i+k]*p[k] ) + dp[0]*p[0] + 1 14 // dp[i] = ( sigma(a[i+k]*p[k]) + p[0] )*dp[0] + sigma(b[i+k]*p[k]) + 1 15 // ***result: 16 // a[i] = sigma(a[i+k]*p[k]) + p[0] 17 // b[i] = sigma(b[i+k]*p[k]) + 1 18 // ***run: 19 // cal a[i] & b[i] 20 // dp[0] = b[0] / (1-a[0]) 21 // 22 // boundary: 23 // set a,b = 0 24 #include <iostream> 25 #include <stdio.h> 26 #include <string.h> 27 #define MAX_N 505 28 #define MAX_K 40 29 30 using namespace std; 31 32 int n,t; 33 int k1,k2,k3; 34 int e1,e2,e3; 35 double p[MAX_K]; 36 double a[MAX_N]; 37 double b[MAX_N]; 38 double dp[MAX_N]; 39 40 void read() 41 { 42 cin>>n>>k1>>k2>>k3>>e1>>e2>>e3; 43 } 44 45 void cal_pro() 46 { 47 memset(p,0,sizeof(p)); 48 p[0]=1.0/(k1*k2*k3); 49 for(int i=1;i<=k1;i++) 50 { 51 for(int j=1;j<=k2;j++) 52 { 53 for(int k=1;k<=k3;k++) 54 { 55 if(i!=e1 || j!=e2 || k!=e3) 56 { 57 p[i+j+k]+=p[0]; 58 } 59 } 60 } 61 } 62 } 63 64 void cal_const() 65 { 66 memset(a,0,sizeof(a)); 67 memset(b,0,sizeof(b)); 68 for(int i=n;i>=0;i--) 69 { 70 for(int k=1;k<=k1+k2+k3;k++) 71 { 72 if(i+k>n) break; 73 a[i]+=a[i+k]*p[k]; 74 b[i]+=b[i+k]*p[k]; 75 } 76 a[i]+=p[0]; 77 b[i]+=1.0; 78 } 79 } 80 81 void solve() 82 { 83 cal_pro(); 84 cal_const(); 85 dp[0]=b[0]/(1.0-a[0]); 86 } 87 88 void print() 89 { 90 printf("%.15f\n",dp[0]); 91 } 92 93 int main() 94 { 95 cin>>t; 96 while(t--) 97 { 98 read(); 99 solve(); 100 print(); 101 } 102 }