【概率DP】 HDU 4089 Activation
题意:有n个人排队等着在官网上激活游戏。Tomato排在第m个。对于队列中的第一个人:
1、激活失败,留在队列中等待下一次激活(概率为p1)
2、失去连接,出队列,然后排在队列的最后(概率为p2)
3、激活成功,离开队列(概率为p3)
4、服务器瘫痪,服务器停止激活,所有人都无法激活了。求服务器瘫痪时Tomato在队列中的位置<=k的概率
思路:
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];
注意特判一种情况。就是p4<eps时候,就不会崩溃了,应该直接输出0
代码:
#include <iostream> #include <cstdio> #include <cmath> using namespace std; double dp[2][2001]; double c[2005], pp[2005]; int main() { int n, m, k; double p1, p2, p3, p4; while ( scanf("%d%d%d", &n, &m, &k) != EOF ) { scanf("%lf%lf%lf%lf", &p1, &p2, &p3, &p4); if ( fabs(1 - p1 - p2) < 1e-9 ) { puts("0.00000"); continue; } double p = p2 / (1 - p1); double p31 = p3 / (1 - p1); double p41 = p4 / (1 - p1); pp[0] = 1; for (int i = 1; i <= n; i++) pp[i] = pp[i-1] * p; dp[1][1] = p4 / (1 - p1 - p2); c[1] = p41; int t = 1; for (int i = 2; i <= n; i++){ t ^= 1; for (int j = 2; j <= k; j++) c[j] = dp[t ^ 1][j-1] * p31 + p41; for (int j = k+1; j <= i; j++) c[j] = dp[t ^ 1][j-1] * p31; double tmp = 0.0; for (int j = i; j > 0; j--) tmp += pp[i-j] * c[j]; dp[t][i] = tmp / (1 - pp[i]); dp[t][1] = p * dp[t][i] + p41; for (int j = 2; j < i; j++) dp[t][j] = p * dp[t][j-1] + c[j]; } printf("%.5f\n", dp[t][m]); } return 0; }