ZOJ - 3329 One Person Game 概率DP 数学消环

题意

有三个色子,每个色子有ki面,分别代表1-ki。

每次你丢三个色子,如果分别摇到a, b, c,则积分器归零, 否则积分器加a+b+c。

积分器大于n时游戏结束。问期望丢的次数。

题解

设d[i] 为当前积分器为i时距离游戏结束还需要丢的期望次数。

d[i] = \sum d[i + j] * p[j]  + d[0]  * p[0] + 1.

p[i]为摇到的和为i的概率。

显然dp方程有环。

即dp[i]既由大于i的dp[i+j]又由小于i的dp[0]转移过来。

现在我们考虑消掉dp[i+j]。

我们设d[i] = A[i] * d[0]  + B[0]。

让d[i+j] = A[i+j] * d[0] + B[i+j]代入原方程,

得到d[i] = \sum (A[i+j] * d[0] * p[j] + B[i+j] * p[j]) + d[0] * p[0] + 1

化简得到d[i] = (\sum (p[j] * A[i + j]) + p[0]) * d[0] + \sum (p[j] * B[i + j]) + 1

A[i] = \sum p[j] * A[i + j] + p[0]

B[i] = \sum p[j] * B[i + j] + 1

又有d[0] = A[0] * d[0] + B[0]

得到d[0] = B[0] / (1 - A[0])

d[0]就是结果

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>

#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 2e6 + 5;

double A[555], B[555];
double p[555];

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    int T;
    cin >> T;
    while(T--) {
        int n, k1, k2, k3, a, b, c;
        scanf("%d %d %d %d %d %d %d", &n, &k1, &k2, &k3, &a, &b, &c);
        memset(p, 0, sizeof(p));
        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) continue;
                    p[i+j+k] += 1./(k1*k2*k3);
                }
        memset(A, 0, sizeof(A));
        memset(B, 0, sizeof(B));
        for(int i = n; i >= 0; i--) {
            A[i] = 1./(k1*k2*k3); B[i] = 1;
            for(int j = 1; j <= k1+k2+k3; j++) {
                A[i] += p[j] * (A[i + j]);
                B[i] += p[j] * (B[i + j]);
            }
        }

        printf("%.15lf\n", B[0] / (1.0 - A[0]));
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/81915155