题意:N个人在排队注册账号,只能队头的人注册,但是服务器不太好会产生以下四种情况:
1.激活失败:此时排队的队形不变,等待下一个时间 此种概率为p1
2.断开连接:此时队头人离开到队尾继续排队,原来在他后面的人前移,概率为p2
3.注册成功:队头人离开 后面的人前移,概率为p3
4.服务器崩溃:大家都没得玩了 概率为p4
想知道在N人排队,且自己排在前k个位置内服务器崩溃的概率。
标记状态: dp[ i ] [ j ] 表示队伍有 i 个人,此时自己在第 j 个
转移的情况:
j==1: dp[i][1]=p1*dp[i][1]+p2*dp[i][i]+p4;
2<=j<=k: dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1]+p4;
k<j<=i: dp[i][j]=p1*dp[i][j]+p2*dp[i][j-1]+p3*dp[i-1][j-1];
然后化简
j==1: dp[i][1]=p*dp[i][i]+p41;
2<=j<=k: dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1]+p41;
k<j<=i: dp[i][j]=p*dp[i][j-1]+p31*dp[i-1][j-1];
然后发现遭遇一个巨恶心的事,不能直接DP,因为两个方向都会受到干扰,所以选择先把dp[i][i]求出来,那么接着dp就可行了。
通过不停的迭代,到最后可以得到dp[ i ] [ i ] 的值,然后一切就OK~
注意若p4过小则可以直接输出0
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 2010
const double eps = 1e-5;
double c[N];
double p[N];
double dp[N][N];
int main(){
int i,j,n,m,k;
double p1,p2,p3,p4;
while(scanf("%d %d %d %lf %lf %lf %lf",&n,&m,&k,&p1,&p2,&p3,&p4)!=EOF){
//
if(p4<eps){
printf("0.00000\n");
continue;
}
double p21=p2/(1.0-p1);
double p31=p3/(1-p1);
double p41=p4/(1-p1);
dp[1][1]=p4/(1-p1-p2);
p[0]=1;
p[1]=p21;
for(i=2;i<=n;i++) p[i]=p21*p[i-1];
for(i=2;i<=n;i++){
c[1]=p41;
for(j=2;j<=k;j++) c[j]=p31*dp[i-1][j-1]+p41;
for(j=k+1;j<=i;j++) c[j]=p31*dp[i-1][j-1];
double temp=0;
for(j=1;j<=i;j++){
temp+=p[i-j]*c[j];
}
dp[i][i]=temp/(1-p[i]);
dp[i][1]=p[1]*dp[i][i]+c[1];
for(j=2;j<i;j++){
dp[i][j]=p21*dp[i][j-1]+c[j];
}
}
printf("%.5lf\n",dp[n][m]);
}
return 0;
}