Loading

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]));
    }
}

参考

posted @ 2020-08-28 11:48  MQFLLY  阅读(140)  评论(0编辑  收藏  举报