思路
典型的一类概率dp问题
逆推,用dp[i]表示从n到i的期望次数,p[i]表示获得i分数的概率,px表示分数清零的概率
容易想到题目中的转移方程为(dp[j]=sum_{i}^{sumk}dp[j+i]p[i]+dp[0]px+1)
显然从dp[n]开始,要求dp[0]
可是每个状态的转移都包含了dp[0],转移出现环了怎么办?
高斯消元?可是n=500,T=300,根本过不去
发现并不每个都互相依赖,而是都依赖于dp[0],那我们找出dp[x]与dp[0]的关系,问题似乎就可以解决了
由于期望的线性性,我们尝试列出一个线性的方程组的形式(或者说就是给dp[0]配个系数)
[dp[x]=a_xdp[0]+b_x
]
把它带入转移方程中,得到
[dp[j]=sum_{i}^{sumk}(a_{i+j}dp[0]+b_{i+j})p[i]+dp[0]px+1
]
尝试把它变形回原来的形式
[dp[j]=dp[0](px+sum_{i}^{sumk}a_{i+j}p[i])+sum_{i}^{sumk}b_{i+j}+1
]
把他和原式(dp[x]=a_xdp[0]+b_x)对应,得到
[a[j]=(px+sum_{i}^{sumk}a_{i+j}p[i])
]
[b[j]=1+sum_{i}^{sumk}b_{i+j}
]
这样a和b就可以逆推出来了
然后dp[0]就等于b[0]/(1-a[0])
然后就好了
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int T,n,k1,k2,k3,ax,bx,cx;
double p[700],P,a[700],b[700];
int main(){
scanf("%d",&T);
while(T--){
memset(p,0,sizeof(p));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
scanf("%d %d %d %d %d %d %d",&n,&k1,&k2,&k3,&ax,&bx,&cx);
P=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++)
if(i!=ax||j!=bx||k!=cx)
p[i+j+k]+=P;
for(int i=n;i>=0;i--){
a[i]=P,b[i]=1;
for(int k=0;k<=k1+k2+k3;k++)
a[i]+=a[i+k]*p[k],b[i]+=b[i+k]*p[k];
}
double ans=b[0]/(1-a[0]);
printf("%.15lf
",ans);
}
return 0;
}