【ZOJ3329】One Person Game

题目描述

有三个骰子,分别有K1,K2,K3个面,一次投掷可以得到三个骰子点数加和的分数,但是,当骰子1等于A,骰子2=B,骰子3=C时,结果清零。问从0开始,分数超过N时投掷次数的期望。


输入

第一行一个数T,表示数据组数。

每组数据对应一行7个数,分别为N,K1,K2,K3,A,B,C (0 <= n <= 500, 1 < K1, K2, K3 <= 6, 1 <= A <= K1, 1 <= B <= K2, 1 <= C <= K3).。


输出

T行,对应每组数据的期望,保留15位小数。


样例输入

2
0 2 2 2 1 1 1
0 6 6 6 1 1 1


样例输出

1.142857142857143
1.004651162790698



题解

本蒟蒻觉得这道题好难啊。

首先,很容易想到设 dp[ i ] 表示当前点数是 i ,还需要投掷次数的期望。于是 dp[ i ] = Σ dp[ i+k ]*p[ k ] +dp[ 0 ]*p[ 0 ] + 1 ,其中 p[ k ] 表示投掷点数和为 k 的概率,特殊的,p[ 0 ] 为投到 a+b+c 的概率 (所以p[ a+b+c ] = 0 )。

那么接下来怎么做呢,我们发现,dp[ i ] 可以表示为一个与 dp[ 0 ] 有关的式子和一个其他式子的和 ,于是设 dp[ i ] = A[ i ] * dp[ 0 ] + B[ i ] ---------- ①,带入上述方程可得:

                                                     dp[ i ] = Σ( A[ i+k ] * dp[ 0 ] * p[ k ] + B[ i+k ] * p[ k ] ) +dp[ 0 ] * p[ 0 ] + 1

                            打开变形:                 dp[ i ] = ( ΣA[ i+k ] * p[ k ] + p[ 0 ] ) * dp[ 0 ] + Σ B[ i+k ] * p[ k ] +1    ----------- ②

我们比较①式和②式,得到:   A[ i ] = Σ A[ i+k ] * p[ k ] + p[ 0 ] , B[ i ] = Σ B[ i+k ] * p[ k ] + 1  。

A[ i ] 和 B[ i ] 可以在O(n*k) 的时间内递推 。 算出 A[ 0 ] 和 B[ 0 ] ,带入 ①式 可得:  dp[ 0 ] = A[ 0 ] * dp[ 0 ] + B[ 0 ] 。

移项化简:   dp[ 0 ] = B[ 0 ] / ( 1 - A[ 0 ] )

 

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long

const int maxn=500+500+100;

int T,a,b,c,k1,k2,k3,n;
double A[maxn],B[maxn],dp[maxn],p[maxn];

template<typename T>void read(T& aa){
    char cc; ll ff;aa=0;cc=getchar();ff=1;
    while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
    if(cc=='-') ff=-1,cc=getchar();
    while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
    aa*=ff;
}

int main(){
    read(T);
    while(T--){
        memset(dp,0,sizeof(0));
        memset(A,0,sizeof(A));
        memset(B,0,sizeof(B));
        memset(p,0,sizeof(p));
        read(n),read(k1),read(k2),read(k3),read(a),read(b),read(c);
        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]+=1.0/(k1*k2*k3);
        for(int i=n;i>=0;i--){
            for(int j=3;j<=k1+k2+k3;j++) A[i]+=A[i+j]*p[j],B[i]+=B[i+j]*p[j];
            A[i]+=1.0/(k1*k2*k3);B[i]+=1;
        }
        printf("%.15lf\n",B[0]/(1.0-A[0]));
    }
    return 0;
}

 

posted @ 2018-08-16 21:16  rld  阅读(133)  评论(0编辑  收藏  举报