【HDU】 4089 Activation 概率DP

题目大意:Tomato要玩一个游戏,他需要排队,一开始这个队列共有N个人,而他在队列的第M个位置,每当有玩家尝试激活登陆游戏时, 会概率性触发四个事件。p1的概率注册失败,队列无变化。p2的概率连接失败,排在队首的人排到队尾。p3的概率成功,队首出队。p4的概率服务器 瘫痪,停止激活!这时候如果排在Tomato前面的人不足K个,那么他会很气愤。问 : Tomato排在第k位以内服务器瘫痪的概率。

令dp[i][j]为一共有i个人的队列Tomato排在第j位服务器瘫痪的概率,通过采用逆推,那么我们可以得到一组状态转移方程:

i == 1 : dp[1][1] = dp[1][1] * p1 + dp[1][1] * p2 + p4;

i  >  1 :

2 <= j <= k : dp[i][j] = dp[i][j] * p1 + dp[i][j - 1] * p2 + dp[i - 1][j - 1] * p3 + p4;//前面不足K个人,Tomato愤怒

k + 1 <= j <= i : dp[i][j] = dp[i][j] * p1 + dp[i][j - 1] * p2 + dp[i - 1][j - 1] * p3;

(这里k可能大于i,我们暂且不管,在下面我们再来消去这里的副作用)

令pp = p2 / (1 - p1) , pp3 = p3 / (1 - p1) , pp4 = p4 / (1 - p1);

令:

i == 1 c[1] = pp4;

2 <= j <= k : c[j] = dp[i - 1][j - 1] * pp3 + pp4;

k + 1 <= j <= i : c[j] = dp[i - 1][j - 1] * pp3;

消去k>i的情况:1 <= j <= i : c[j] = dp[i - 1][j - 1] * pp3 + (j <= k ? pp4 : 0);(这里我们默认dp[0][j]已经初始化)

进而化简得到:

j == 1 : dp[i][1] = dp[i][i] * p + c[1]; (1)

2 <= j <= i : dp[i][j] = dp[i][j - 1] * pp2 + c[j];

由递推关系可知 :dp[i][i] = dp[i][1] * p ^ (i - 1) + tmp;   (2)

其中tmp = c[1] ^ (i - 1) + c[2] ^ (i - 2) + ... + c[i - 1];

联立(1)、(2) 消去dp[i][1]得 : dp[i][i] = tmp / (1 - p ^ i); (也可以消去dp[i][i])

回代得原式     :2 <= j <  i:    dp[i][j] = dp[i][j - 1] * p + c[j];

最终解即为dp[n][m]。


代码如下:


//HDU 4089 Activation 概率DP
/*
    p = p2 / (1 - p1)
    p31 = p3 / (1 - p1)
    p41 = p4 / (1 - p1)

    i = 1:           c[1] = pp4, dp[1][1] = pp4 / (1 - p)

             j == 1:c[1] = pp4;
        2 <= j <= k:c[j] = dp[i - 1][j - 1] * pp3 + pp4;(1)
    k + 1 <= j <= i:c[j] = dp[i - 1][j - 1] * pp3;      (2)
    联立(1)、(2) 得:2 <= j <= i:    c[j] = dp[i - 1][j - 1] * pp3 + (i <= k ? pp4 : 0);

    易知:
             j == 1:dp[i][1] = dp[i][i] * p + c[1];     (3)
        2 <= j <= k:dp[i][j] = dp[i][j - 1] * p + c[j]; (4)
    k + 1 <= j <= i:dp[i][j] = dp[i][j - 1] * p + c[j]; (5)
    联立(4)、(5) 得:2 <= j <= i:    dp[i][j] = dp[i][j - 1] * p + c[j];

    由递推关系可知 :dp[i][i] = dp[i][1] * pow(p, i - 1) + tmp;   (6)
    其中tmp = c[1] ^ (i - 1) + c[2] ^ (i - 2) + ... + c[i - 1];
    联立(3)、(6) 得:dp[i][i] = tmp / (1 - pow(p, i));
    回代得原式     :2 <= j <  i:    dp[i][j] = dp[i][j - 1] * p + c[j];

    最终解即为dp[n][m];
*/

#include <stdio.h>
#include <string.h>
#define REP(i, a, b) for(int i = a; i <= b; ++i)
const double eps = 1e-5;
const int O = 2005;
double pp[O], dp[O][O], c[O], p1, p2, p3, p4, p, pp3, pp4;
int n, m, k;
void work(){
    if(1 - p1 - p2 < eps){//特判除数等于0的情况
        printf("%.5f\n", 0);
        return;
    }
    if(p4 < eps){//如果已经小于0.00001则直接输出0.00000
        printf("%.5f\n", 0);
        return;
    }
    p = p2 / (1 - p1);
    pp3 = p3 / (1 - p1);
    pp4 = p4 / (1 - p1);
    pp[0] = 1.0;
    REP(i, 0, n) dp[0][i] = dp[i][i] = 0;
    REP(i, 1, n) pp[i] = p * pp[i - 1];
    dp[1][1] = p4 / (1 - p1 - p2);
    REP(i, 2, n){
        REP(j, 1, i) c[j] = dp[i - 1][j - 1] * pp3 + (j <= k ? pp4 : 0);
        REP(j, 1, i) dp[i][i] += c[j] * pp[i - j];
        dp[i][i] /= (1 - pp[i]);
        dp[i][1] = dp[i][i] * p + c[1];
        REP(j, 2, i - 1) dp[i][j] = dp[i][j - 1] * p + c[j];
    }
    printf("%.5f\n", dp[n][m]);
}
int main(){
    while(~scanf("%d%d%d%lf%lf%lf%lf", &n, &m, &k, &p1, &p2, &p3, &p4)) work();
    return 0;
}


 

你可能感兴趣的:(dp,HDU)