题意: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; }