HDU 4089 Activation(概率DP)

题目: LINK

题意:
有n人要激活游戏,n个人排成一个队列(Tomato最初排名为m),
对于队列中的第一个人,在激活的时候有以下4种情况:
    1.激活失败:留在队列中继续等待下一次激活(概率p1)
    2.失去连接:激活失败,出队列然后排到队列的尾部(概率p2)
    3.激活成功:出队列(概率p3)
    4.服务挂了:服务器停止服务了,所有人都无法激活了(概率p4)
求服务器挂了,此时Tomato的排名<=k的概率
这个求概率有点类似期望的求法。 
dp[i][j]表示队列一共i个人, 当前tomato处于第j个,达到结果的概率
j == 1,     dp[i][j] = p1 * dp[i][j] + p2 * dp[i][i] + p4; 
1 < 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]; 
化简可以得到
令pp2 = p2 / ( 1 - p1) ,  pp3 = p3 / (1 - p1) , pp4 = p4 / (1 - p1) 
j == 1,     dp[i][j] = pp2 * dp[i][i] + pp4 ;
1 < j <= k, dp[i][j] = pp2 * dp[i][j-1] + pp3 * dp[i-1][j-1] + pp4; 
k < j <= i, dp[i][j] = pp2 * dp[i][j-1] + pp3 * dp[i-1][j-1]; 
dp[i][j] 是i 一层一层的求的在求dp[i][]时dp[i-1][]已经求出来了。
所以可以写成 
dp[i][1] = pp2 * dp[i][i] + C[1]; 
dp[i][2] = pp2 * dp[i][1] + C[2]; 
dp[i][3] = pp2 * dp[i][2] + C[3]; 
.......
dp[i][i] = pp2 * dp[i][i-1] + C[i]; 
这个式子可以不断代入求的dp[i][]的解,最后输出dp[n][m]即可。
注意一个WAWAWAWAWA的地方 特判断p1 + p2 接近1的时候直接输出0.00000,因为这种情况下题目要求的情况出现概率几乎为0,但是算的话会出现错误。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
using namespace std; 
#define INF 1000000000
//typedef __int64 LL; 
#define N 2010
int n, m, k; 
double p1, p2, p3, p4; 
double pp1, pp2, pp3, pp4; 
double dp[N][N], C[N]; 

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin); 
#endif // ONLINE_JUDGE
    while(scanf("%d%d%d%lf%lf%lf%lf", &n, &m, &k, &p1, &p2, &p3, &p4) != EOF) {
        if(1.0 - p1 - p2 < 1e-9) {
             puts("0.00000"); continue; // this ? !
        }
        pp2 = p2 / (1.0 - p1); 
        pp3 = p3 / (1.0 - p1); 
        pp4 = p4 / (1.0 - p1); 
        memset(dp, 0, sizeof(dp)); 
        dp[1][1] = pp4 / (1.0 - pp2); 
        double a, b; 
        for(int i = 2; i <= n; i++) {
            a = pp2; b = pp4; 
            C[1] = pp4; 
            for(int j = 2; j <= i; j++) {
                double aa, bb; 
                if(j <= k) {
                    C[j] = pp3 * dp[i-1][j-1] + pp4; 
                }
                else {
                    C[j] = pp3 * dp[i-1][j-1]; 
                }
                aa = a * pp2; bb = b * pp2 + C[j]; 
                a = aa; b = bb; 
            }
            dp[i][i] = b / (1.0 - a); 
            double temp = dp[i][i]; 
            for(int j = 1; j < i; j++) {
                dp[i][j] = pp2 * temp + C[j]; 
                temp = dp[i][j]; 
            }
        }
        printf("%.5lf\n", dp[n][m]); 
    }    
    
    return 0; 
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值