题目大意:一款新游戏注册账号时,有n个用户在排队。每处理一个用户的信息时,可能会出现下面四种情况:
1.处理失败,重新处理,处理信息仍然在队头,发生的概率为p1;
2.处理错误,处理信息到队尾重新排队,发生的概率为p2;
3.处理成功,队头信息处理成功,出队,发生的概率为p3;
4.服务器故障,队伍中所有信息丢失,发生的概率为p4;
小明现在在队伍中的第m个位置,问当他前面的信息条数不超过k-1时服务器故障的概率。
题目分析:这道题的状态转移方程不难写。定义状态dp(i,j)表示在有 i 个人的队伍中,他排在第 j 个位置时到达要求状态的概率。则状态转移方程为:
dp(i,1)=p1*dp(i,1)+p2*dp(i,i)+p4
dp(i,j)=p1*dp(i,j)+p2*(i,j-1)+p3*dp(i-1,j-1)+p4 (2<=j<=k)
dp(i,j)=p1*dp(i,j)+p2*(i,j-1)+p3*dp(i-1,j-1) (k<j<=i)
整理一下,并另p21=p2/(1-p1),p31=p3/(1-p1),p41=p4/(1-p1),则得到:
dp(i,1)=p21*dp(i,i)+p41
dp(i,j)=p21*dp(i,j-1)+p31*dp(i-1,j-1)+p41 (2<=j<=k)
dp(i,j)=p21*dp(i,j-1)+p31*dp(i-1,j-1) (k<j<=i)
这样就可以通过递推求解。
为了书写方便,把上面的三个转移方程用两个方程表示出来:
dp(i,1)=p21*dp(i,i)+c(1)
dp(i,j)=p21*dp(i,j-1)+c(j) (2<=j<=i)
dp(i,i)可以通过迭代得到:
(1-p21^i)dp(i,i)=∑(p21^(i-j))*c(j) (1<=j<=i)
ps:得加特判,否则会WA!!。。。
代码如下:
# include<iostream> # include<cstdio> # include<cmath> # include<cstring> # include<algorithm> using namespace std; const double eps=1e-5; int n,m,k; double p1,p2,p3,p4; double dp[2005][2005]; int main() { while(~scanf("%d%d%d",&n,&m,&k)) { scanf("%lf%lf%lf%lf",&p1,&p2,&p3,&p4); if(p4<eps){ printf("0.00000\n"); continue; } double p21=p2/(1-p1); double p31=p3/(1-p1); double p41=p4/(1-p1); dp[1][1]=p41/(1-p21); for(int i=2;i<=n;++i){ dp[i][i]=0; for(int j=1;j<=i;++j){ if(j==1) dp[i][i]+=pow(p21,i-j)*p41; else if(j>=2&&j<=k) dp[i][i]+=pow(p21,i-j)*(p31*(dp[i-1][j-1])+p41); else dp[i][i]+=pow(p21,i-j)*p31*dp[i-1][j-1]; } dp[i][i]/=(1-pow(p21,i)); for(int j=1;j<i;++j){ if(j==1) dp[i][j]=p21*dp[i][i]+p41; else if(j>=2&&j<=k) dp[i][j]=p21*dp[i][j-1]+p31*dp[i-1][j-1]+p41; else dp[i][j]=p21*dp[i][j-1]+p31*dp[i-1][j-1]; } } printf("%.5lf\n",dp[n][m]); } return 0; }