ZOJ 3329 One Person Game 概率DP

题目大意:

就是现在有一个游戏初始的时候分值为0,现在有3个骰子,每次同时掷骰子,当三个骰子依此出现的点数是a,b,c时将得分重新变为0,否则就加上这个点数和,三个骰子的最大点数分别是K1, K2, K3并且出现1~Ki的可能性是1/Ki, (1 <= i <= 3)

现在要求分数不低于n,问最少要掷多少次骰子, 求这个次数的期望。


大致思路:

思路见代码注释


代码如下:

Result  : Accepted     Memory  :  280 KB     Time  :  0 ms

/*
 * Author: Gatevin
 * Created Time:  2014/11/30 18:01:38
 * File Name: Asuna.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

/*
 * 首先如果用E[i]表示当前分数为i时还需要的步数的期望, 则答案即为E[0]
 * 不难发现E[i > n] = 0;
 * 而且对于E[i < n], E[i] = E[i + 3]*p[3] + E[i + 4]*p[4] + ... + E[i + K1 + K2 + K3]*p[K1 + K2 + K3] + E[0]*p[0] + 1;(0 <= i <= n)
 * 其中p[j] 表示掷骰子之后加的分数,p[0]表示重置为0的概率
 * 不难发现这个方程有环, 但是由于一共有 n + 1 个未知数(E[0 ~ n])且有 n + 1个方程,矩阵的秩满,可解
 * 在仔细观察,由于E[i > n] = 0, E[n] = E[0]*p[0] + 1, E[n - 1] = E[0]*p[0] + 1, ... E[n - 4] = E[n - 3]*p[3] + E[0]*p[0] + 1....
 * 由于E[n, n -1, n - 2, n - 3, n - 4..]都是A*E[0] + B的形式,在递推时 E[k] = A[k]*E[0] + B[k]也一定满足(一次的线性关系)
 * 并且设E[i] = A[i]*E[0] + B[i]之后,由于 E[i] 满足的递推式有
 * A[i]*E[0] + B[i] = (A[i + 3]*E[0] + B[i + 3])*p[3] + .... + (A[i + K1 + K2 + K3]*E[0] + B[i + K1 + K2 + K3])*p[i + K1 + K2 + K3] + E[0]*p[0] + 1
 * 于是, A[i] = A[i + 3]*p[3] + A[i + 4]*p[4] + .. + A[i + K1 + K2 + K3]*p[K1 + K2 + K3] + p[0];
          B[i] = B[i + 3]*p[3] + B[i + 4]*p[4] + .. + B[i + K1 + K2 + K3]*p[K1 + K2 + K3] + 1;
 * A[i > n] = B[i > n] = 0;
 * 于是可以递推算出A[0], B[0];
 * 由于A[0]*E[0] + B[0] = E[0]
 * E[0] = B[0]/(1 - A[0]);
 */

double A[520], B[520];
int k1, k2, k3, t, n, a, b, c;
double p[20];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d %d %d %d %d %d %d", &n, &k1, &k2, &k3, &a, &b, &c);
        memset(p, 0, sizeof(p));
        p[0] = 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++)
                    p[i + j + k] += p[0];
        p[a + b + c] -= p[0];
        memset(A, 0, sizeof(A));
        memset(B, 0, sizeof(B));
        for(int i = n; i >= 0; i--)
        {
            for(int up = 3; up <= k1 + k2 + k3; up++)
            {
                A[i] += A[i + up]*p[up];
                B[i] += B[i + up]*p[up];
            }
            A[i] += p[0];
            B[i] += 1;
        }
        double ans = B[0]/(1 - A[0]);
        printf("%.10f\n", ans);
    }
    return 0;
}


你可能感兴趣的:(ZOJ,one,game,person,概率DP,3329)