ZOJ 3329 One Person Game

题意:给定n,k1,k2,k3,a,b,c七个数。有三个骰子,第一个骰子有k1个面,第二个有k2个面,第三个有k3个面,摇骰子得到的点数即为摇动后向上的面的点数,每一个面向上的概率相同。一个人的分数记为count,初始时count=0,然后同时摇三个骰子记为一次摇动,每次摇动之后,三个骰子分别得到x1,x2,x3点,若x1=a且x2=b且x3=c,则count=0,否则,count += x1+x2+x3。若count > n,则游戏结束,否则继续游戏。问摇动次数的期望。

解法:在做了POJ 2096 Collecting Bugs之后,已经大概懂得怎样用DP处理期望问题了。DP求概率要正推,求期望要倒推。

   但是,当我写出状态转移的方程的时候,又发现了一个新问题,就是这道题是带环的期望问题。一般而言,带环的问题都要用高斯消元来做,但这道题其实不用那么麻烦。

   设E[i]表示count = i的情况下,平均还需要摇多少次骰子,才能结束游戏。p[i]表示摇骰子后count需要加i分的概率,p[0]表示摇骰子后count置0的概率。

   状态转移方程为:E[i] = E[i+1]*p(1) + E[i+2]*p(2) + E[i+3]*p[3].....+E[i+k]*p[k] + E[0]*p[0] + 1。

   可以发现,每个状态转移方程的未知量都是E[0],所以这道题就有了下面解法:

   设E[i] = a[i]*E[0] + b[i],则由上面的状态转移方程又有E[i] = (segma(a[i+x]*p[x]) + p[0]) * E[0] + segma(b[[i+x]*p[x]) + 1。

   所以有a[i] = segma(a[i+x]*p[x]) + p[0],b[i] = segma(b[i+x]*p[x]) + 1。而答案E[0] = b[0] / (1-a[0])。这样就可以用DP解决问题了。

tag:math, 概率DP, 代数变形

 1 /*
 2  * Author:  Plumrain
 3  * Created Time:  2013-10-30 09:57
 4  * File Name: math-ZOJ-3329.cpp
 5  */
 6 #include<iostream>
 7 #include<cstdio>
 8 #include<cstring>
 9 
10 using namespace std;
11 
12 #define CLR(x) memset(x, 0.0, sizeof(x))
13 
14 int n, tot;
15 double p[20], a[1000], b[1000];
16 
17 void init()
18 {
19     int k[3], tmp, sum = 0;
20     scanf ("%d", &n);
21     for (int i = 0; i < 3; ++ i)
22             scanf ("%d", &k[i]);
23     for (int i = 0; i < 3; ++ i){
24         scanf ("%d", &tmp);
25         sum += tmp;
26     }
27 
28     int num[20]; CLR (num);
29     for (int i = 1; i <= k[0]; ++ i)
30         for (int j = 1; j <= k[1]; ++ j)
31             for (int l = 1; l <= k[2]; ++ l)
32                 ++ num[i+j+l]; 
33     -- num[sum];
34 
35     int mul = k[0] * k[1] * k[2];
36     tot = k[0] + k[1] + k[2];
37     num[0] = 1;
38     for (int i = 0; i <= tot; ++ i)
39         p[i] = (double)num[i] / mul;
40 }
41 
42 double DP()
43 {
44     CLR (a); CLR (b);
45     for (int i = n; i >= 0; -- i){ 
46         a[i] = p[0];
47         b[i] = 1.0;
48         for (int j = 1; j <= tot; ++ j){
49             a[i] += a[i+j] * p[j];
50             b[i] += b[i+j] * p[j];
51         }
52     }
53     
54     return b[0] / (1 - a[0]);
55 }
56 
57 int main()
58 {
59     int T;
60     scanf ("%d", &T);
61     while (T--){
62         init();
63         printf ("%.10f\n", DP());
64     }
65     return 0;
66 }
View Code

 

posted @ 2013-10-30 10:56  Plumrain  阅读(351)  评论(0编辑  收藏  举报