题目:仙5的激活序列。有以下4种情况:
1、注册失败,但是不影响队列顺序 ,概率为p1
2、连接失败,队首的人排到队尾,概率为p2
3、注册成功,队首离开队列,概率为p3
4、服务器崩溃,激活停止,概率为p4
求主角的位置在K以内,而且服务器崩溃的概率
http://acm.hdu.edu.cn/showproblem.php?pid=4089
题解:
是一个概率题,分析一下题意后发现和“dp求期望”的题目有点像,因为其中都有一种死循环的可能,
该题中,如果总是发生p1概率的情况那就是死循环了。然后想到一个二维dp:
dp[i][j]表示队列中有i个人,Tomato排在第j个,能发生所求事件的概率。
显然,dp[n][m]即为所求。
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;
j > k : 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;
j > k : dp[i][j] = p*dp[i][j-1] + p31*dp[i-1][j-1];
其中:
p = p2 / (1 - p1);
p31 = p3 / (1 - p1);
p41 = p4 / (1 - p1);
现在可以循环 i = 1 -> n 递推求解dp[i],所以在求dp[i]时,dp[i-1]就相当于常数了,
设dp[i][j]的常数项为c[j]:
j == 1 : dp[i][1] = p*dp[i][i] + c[1];
2<=j<=k: dp[i][j] = p*dp[i][j-1] + c[j];
j > k : dp[i][j] = p*dp[i][j-1] + c[j];
在求dp[i]时,就相当于求“i元1次方程组”:
dp[i][1] = p*dp[i][i] + c[1];
dp[i][2] = p*dp[i][1] + c[2];
dp[i][3] = p*dp[i][2] + c[3];
...
dp[i][i] = p*dp[i][i-1] + c[i];
先解出dp[i][i ],进而可以得到dp[i][1],剩下的就可以递推出来了
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define ll long long
#define prt(k) cerr<<#k" = "<<k<<endl
const int N = 2014;
double dp[N][N];
double p1, p2, p3, p4;
int n, m, K;
int main()
{
while(cin>>n>>m>> K >> p1>>p2>>p3>>p4)
{
if(p3 + p4 < 1e-8) { puts("0.00000"); continue; }
memset(dp, 0, sizeof dp);
dp[1][1] = p4 / (p3 + p4);
// prt(dp[1][1]);
p2 /= 1 - p1;
p3 /= 1 - p1;
p4 /= 1 - p1;
double pp[N], c[N];
// prt(p2); prt(p3);
c[1] = p4;
pp[0] = 1;
for(int i=1;i<=n;i++) pp[i]=pp[i-1]*p2; ///pp[i] = p2 ^ i
for(int i=2;i<=n;i++)
{
for (int j=2;j<=K;j++)
c[j] = dp[i-1][j-1] * p3 + p4;
for (int j=K+1;j<=i;j++)
c[j] = dp[i-1][j-1] * p3;
double tmp = 0;
for (int j=1; j<=i; j++)
tmp += pp[i-j] * c[j];
dp[i][i] = tmp / (1 - pp[i]);
dp[i][1] = dp[i][i] * p2 + p4;
for(int j=2;j<i;j++)
dp[i][j] = dp[i][j-1] * p2 + c[j];
}
printf("%.5f\n", dp[n][m]);
}
}