标签:pre ems 状态 ret for 获得 xdp 等于 +=
典型的一类概率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\n",ans);
}
return 0;
}
标签:pre ems 状态 ret for 获得 xdp 等于 +=
原文地址:https://www.cnblogs.com/dreagonm/p/10527521.html