题意:
n个人排队激活,对于队列中的第一个人,在激活的时候有以下五种情况:
1.激活失败:留在队列中继续等待下一次激活(概率p1)
2.失去连接:激活失败,并且出队列然后排到队列的尾部(概率p2)
3.激活成功:出队列(概率p3)
4.服务器瘫:服务器停止服务了,所有人都无法激活了(概率p4)
求服务器瘫痪并且此时Tomato在队伍的位置在<=k的概率,也就是前面最多有k-1个人
分析:
dp[i][j]:队伍一共有i个人,排在第j位置,达到目标的概率
当j==1时,dp[i][1]=dp[i][1]*p1+dp[i][i]*p2+dp[i][0]*p3+p4;dp[i][0]=0;
当1 < j<=k时,dp[i][j]=dp[i][j]*p1+dp[i][j-1]*p2+dp[i-1][j-1]*p3+p4
当j> k时,dp[i][j]=dp[i][j]*p1+dp[i][j-1]*p2+dp[i-1][j-1]*p3;
然后化简一下使
p21=p2/(1-p1);
p31=p3/(1-p1);
p41=p4/(1-p1);
当j==1时,dp[i][1]=dp[i][i]*p21+p41
当1 < j<=k时,dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31+p41
当j> k时,dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31;
为了简单点,可以用c[j]来表示之前求出来的值,比如c[1]=p41,当1 < j<=k时,c[j]=dp[i-1][j-1]*p31+p41,当j> k时,c[j]=dp[i-1][j-1]*p31;
然后递推就行了。然后发现在求dp[i][1]的时候要用到dp[i][i],成环了,高斯?
这里不用,可以直接迭代,求出dp[i][i]。
关于特判问题/如果不加的话会wa,或者T。
有可能计算过程中分母非常接近0,于是计算结果出现NaN,代码继续用NaN参与计算就会变得非常慢。
所以,一定注意每一步会不会出现分母为0 的情况,以及预处理的时候对于一些特殊情况导致自己的式子会出现分母为0的情况
using namespace std;
double p1,p2,p3,p4,p21,p31,p41;
double dp[2005][2005],c[2005];
int n,m,k;
int main(){
while(scanf("%d%d%d%lf%lf%lf%lf",&n,&m,&k,&p1,&p2,&p3,&p4)!=EOF){
if(abs(1-p1-p2)<1e-5){printf("0.00000\n");continue;}
p21=p2/(1-p1);
p31=p3/(1-p1);
p41=p4/(1-p1);
dp[1][1]=p41/(1-p21);
for(int i=2;i<=n;i++)
{
int t=k;if(i<k) t=i;
c[1]=p41;
for(int j=2;j<=t;j++)
c[j]=dp[i-1][j-1]*p31+p41;
for(int j=k+1;j<=i;j++)
c[j]=dp[i-1][j-1]*p31;
double tmp=0,p=1;
for(int j=i;j>=1;j--)
{
tmp+=c[j]*p;
p=p*p21;
}
dp[i][i]=(tmp)/(1-p);
dp[i][1]=dp[i][i]*p21+c[1];
for(int j=2;j<i;j++)
dp[i][j]=dp[i][j-1]*p21+c[j];
}
printf("%.5f\n",dp[n][m]);
}
return 0;
}