ZOJ-3329 One Person Game
ZOJ-3329 One Person Game
题意
有三个骰子,分别有\(k1,k2,k3\) 个标号\(1-k\) 的面,每次扔骰子,若三个面分别为\(a,b,c\) 则分数置为0,否则加上分数之和。
当分数大于0时游戏结束,问游戏结束的期望步数。
分析
容易想到转移方程,令\(dp[i]\) 表示当前分数为\(i\) 时,游戏结束的期望步数,则答案就是\(dp[0]\)
转移方程
\[dp[i] = \sum p_k \cdot dp[i + k] + p_0 \cdot dp[0] + 1
\]
一般成环的转移方程考虑用高斯消元求解,但是此题的特殊性在于所有状态都和\(dp[0]\) 有两边,如果能直接把\(dp[0]\) 表示出来此题就结束了。
故不妨设\(dp[i] = A[i] * dp[0] + B[i]\)
代入转移方程有
\[dp[i] = \sum p_k \cdot (A[i + k] \cdot dp[0]+B[i+k])+p_0*dp[0]+1
\\
=\sum (p_k \cdot A[i+k]+p_0)\cdot dp[0]+\sum (p_k \cdot B[i+k]) + 1
\]
于是有\(A[i] = \sum p_k \cdot A[i+k] + p_0,B[i] = \sum p_k \cdot B[i+k] + 1\)
于是递推求得\(A,B\) 即可
代码
double A[550], B[550];
double p[100];
int main() {
int T = readint();
int k1, k2, k3;
int a, b, c, n;
while (T--) {
memset(p, 0, sizeof p);
n = readint();
k1 = readint();
k2 = readint();
k3 = readint();
a = readint();
b = readint();
c = readint();
double p0 = 1.0 / (k1 * k2 * k3);
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;
for (int i = 0; i <= n; i++) A[i] = p0, B[i] = 1;
for (int i = n + 1; i < 550; i++) A[i] = B[i] = 0;
for (int i = n; i >= 0; i--)
for (int j = 1; j <= k1 + k2 + k3; j++)
A[i] += A[i + j] * p[j], B[i] += B[i + j] * p[j];
printf("%.8f\n", B[0] / (1 - A[0]));
}
}