题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3329
思路:设maxn=k1+k2+k3,那么摇一次骰子的点数在3-maxn之间,p[i]表示骰子的点数为i的概率(不含第一,二,三个的骰子点数分别为a,b,c此时会清空),清空的概率p0=1/k1/k2/k3.
f[i]为当前骰子总点数为i到点数大于n的期望次数,初始时i=0,当i>n时,f[i]=0.
k=maxn
f[i]=(∑ f[i+k]* p[k])+p0*f[0]+1①
k=3
每次都有f[0]是一个循环,无法正常递推求解。
我们设f[i]=A[i]f[0]+B[i] ②
f[i+k]=A[i+k]*f[0]+B[i]③
将3式代入1式得,f[i]=(∑A[i+k]*p[k]+p0)*f[0]+∑B[i+k]*p[k]+1
因此我们发现A[i]=∑A[i+k] * p[k]+p0 B[i]=∑B[i+k] * p[k]+1
在当i>n时,f[i]=0.,那么A[i]=B[i]=0
所以我们从n一直递推到1求A[i],B[i]即可。
f[0]=A[0]*f[0]+B[0]
f[0]=B[0]/(1-A[0])
#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <iomanip>
#include <stdio.h>
#include <string>
#include <queue>
#include <cmath>
#include <stack>
#include <map>
#include <set>
using namespace std;
double A[506],B[506],p[20];
int main()
{
int t;
scanf("%d",&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);
double p0;
p0=1.0/(k1*k2*k3);
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++)
{
int x=i+j+k;
p[x]+=p0;
}
}
}
int x=a+b+c;
p[x]-=p0;
x=k1+k2+k3;
/*for(int i=1;i<=x;i++)
{
printf("%lf\n",p[i]);
}*/
for(int i=n;i>=0;i--)
{
A[i]=p0; B[i]=1;
for(int j=3;j<=x;j++)
{
int x1=i+j;
if(x1>n);
else
{
A[i]+=p[j]*A[x1];
B[i]+=p[j]*B[x1];
}
}
}
double ans=B[0]/(1-A[0]);
printf("%.15lf\n",ans);
}
return 0;
}