题目描述
有三个骰子,分别有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 ",B[0]/(1.0-A[0])); } return 0; }