首页 > 代码库 > [ACM] ZOJ 3329 One Person Game (概率DP,有环,巧妙转化)
[ACM] ZOJ 3329 One Person Game (概率DP,有环,巧妙转化)
There is a very simple and interesting one-person game. You have 3 dice, namely Die1, Die2 and Die3. Die1 has K1 faces. Die2 has K2 faces. Die3 has K3 faces. All the dice are fair dice, so the probability of rolling each value, 1 to K1, K2, K3 is exactly 1 / K1, 1 / K2 and 1 / K3. You have a counter, and the game is played as follow:
- Set the counter to 0 at first.
- Roll the 3 dice simultaneously. If the up-facing number of Die1 is a, the up-facing number of Die2 is b and the up-facing number of Die3 is c, set the counter to 0. Otherwise, add the counter by the total value of the 3 up-facing numbers.
- If the counter‘s number is still not greater than n, go to step 2. Otherwise the game is ended.
Calculate the expectation of the number of times that you cast dice before the end of the game.
Input
There are multiple test cases. The first line of input is an integer T (0 < T <= 300) indicating the number of test cases. Then T test cases follow. Each test case is a line contains 7 non-negative integers n, K1, K2, K3, a, b, c (0 <= n <= 500, 1 < K1, K2, K3 <= 6, 1 <= a <= K1, 1 <= b <= K2, 1 <= c <= K3).
Output
For each test case, output the answer in a single line. A relative error of 1e-8 will be accepted.
Sample Input
2 0 2 2 2 1 1 1 0 6 6 6 1 1 1
Sample Output
1.142857142857143 1.004651162790698
Author: CAO, Peng
Source: The 7th Zhejiang Provincial Collegiate Programming Contest
解题思路:
有三枚骰子(下面用dice1,dice2, dice3表示,骰子太难拼),每一枚dice分别有k1,k2,k3个面,每个dice每个面上标的数字分别为 1到k1, 1到k2 , 1到k3 ,每个dic都是均匀的。现在玩一个游戏,有一个计分器,初始为0,每次同时投三枚dice,如果dice1最上面显示的数字是a,dice2最上面显示的数字是b,dice3最上面显示的数字为c,那么计分器清0,否则计分器加上三枚dice最上面显示的数字之和,当计分器的分数>n时,游戏结束,求平均投的dice次数。
本题关系有环,不能直接使用递推..需要转化
下面思路转载:http://blog.csdn.net/morgan_xww/article/details/6775853 思路太棒了!括号里是我自己加的解释
设 E[i]表示现在分数为i,到结束游戏所要掷骰子的次数的期望值。
显然 E[>n] = 0; E[0]即为所求答案;
E[i] = ∑Pk*E[i+k] + P0*E[0] + 1;
Pk表示点数和为k的概率,P0表示分数清零的概率
(当前分数i掷骰子以后有两种情况,一是k是下一次掷骰子得到的分数,且不会清0,k需要累加,因为会出现很多种 分数,二是下一次掷骰子正好投出需要被清0的那种情况)
由上式发现每个 E[i]都包含 E[0],而 E[0]又是我们要求的,是个定值。
设 E[i] = a[i]*E[0] + b[i];
将其带入上面的式子:
E[i] = ( ∑Pk*a[i+k] + P0 )*E[0] + ∑Pk*b[i+k] + 1; (自己手动带入做一遍,看能不能得到这个式子)
显然,
a[i] = ∑Pk*a[i+k] + P0;
b[i] = ∑Pk*b[i+k] + 1;
当 i > n 时:
E[i] = a[i]*E[0] + b[i] = 0;
所以 a[i>n] = b[i>n] = 0;
可依次算出 a[n],b[n]; a[n-1],b[n-1] ... a[0],b[0]; (递推的关系就可以求出,从后往前)
则 E[0] = b[0]/(1 - a[0]); (最后答案)
代码:
#include <iostream> #include <stdio.h> #include <iomanip> #include <string.h> using namespace std; const int maxn=520; double A[maxn]; double B[maxn]; double p[maxn];//p[i]保存出现得分为i的概率 double p0;//三个dice任意转出三个数字和出现的概率 int n,k1,k2,k3,a,b,c; void getP() { for(int i=1;i<=k1;i++) for(int j=1;j<=k2;j++) for(int k=1;k<=k3;k++) { if(i!=a||j!=b||k!=c) p[i+j+k]+=p0;//因为出现i+j+k这个分数的概率为p0,这个分数可以出现过多次 } } int main() { int t;cin>>t; while(t--) { cin>>n>>k1>>k2>>k3>>a>>b>>c; memset(p,0,sizeof(p)); memset(A,0,sizeof(A)); memset(B,0,sizeof(B)); p0=1.0/(k1*k2*k3); getP(); for(int i=n;i>=0;i--)//原始分数 { for(int j=3;j<=k1+k2+k3;j++)//三枚dice出现的分数 { A[i]+=p[j]*A[i+j];//递推公式 B[i]+=p[j]*B[i+j]; } A[i]+=p0; B[i]+=1; } cout<<setiosflags(ios::fixed)<<setprecision(15)<<B[0]/(1-A[0])<<endl; } return 0; }