HDU4089 Activation 概率DP

本文详细解析了HDU4089激活问题,采用概率动态规划方法,探讨了在特定条件下Tomato在队列中的位置及概率问题。通过对不同位置状态的分析,得出状态转移方程,最终求解出当服务器崩溃时,Tomato在队列中且位置序号小于等于k的概率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HDU 4089 Activation 概率DP

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=4089]

题目大意:

Tomato去排队激活游戏,每次对队首位置的人进行处理,处理时会产生四种情况:

  1. 激活失败,队伍不变,下次重新从队首的人开始处理,概率为p1
  2. 连接失败,队首的人排到队尾,下次从新的队首的人开始处理,概率为p2
  3. 激活成功,队首的人退出队伍,下次从队列中下一个人开始处理,概率为p3
  4. 服务器崩溃,GG,概率为p4

已知队列总人数为n,Tomato当前在队列中的位置为m,问当服务器崩溃时Tomato在队列中且当前位置序号小于等于k的概率是多少。

分析:

一道求数学期望的题目,利用概率DP,首先找出可行的状态转移方程,DP[ i ][ j ]表示当前队列中有 i 个人,Tomato排在 j 号位,当前情况下符合条件的概率为多少。
对于排在队列中的不同的位置时,有不同的表示:
j==1:dp[ i ][ j ] = p1 * dp[ i ][ j ] + 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 ]

将上述表达式简化:
p = p2/(1-p1)
p31 = p3/(1-p1)
p41 = p4/(1-p1)

j==1:dp[ i ][ j ] = 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 ]

从 i = 1->n 循环递推算出每一项dp[ i ],此时dp[i - 1]已经在之前的计算中得到了,所以可以把除了dp[ i ][ j∈[1,i] ]之外的所有项归为常数项
即表示为:
j==1:dp[ i ][ j ] = p * dp[ i ][ i ] + C[ j ]
2<=j<=k:dp[ i ][ j ] = p * dp[ i ][ j-1 ] + C[ j ]
k<j<=i:dp[ i ][ j ] = p * dp[ i ][ j-1 ] + C[ j ]

对于每一次循环,首先要得到dp[ i ][ 1 ]的值
dp[ i ][ 1 ]可以用递推公式进行递推得到:
dp[ i ][ 1 ] = p * dp[ i ][ i ] + c[ 1 ]
dp[ i ][ i ] = p * dp[ i ][ i-1 ] + c[ i ]
dp[ i ][ i-1 ] = p * dp[ i ][ i-2 ] + c[ i-1 ]

dp[ i ][ 2 ] = p * dp[ i ][ 1 ] + c[ 2 ]
所以整理之后可以用一个式子表达dp[ i ][ 1 ]:
d p [ i ] [ 1 ] = C [ 1 ] + ∑ n = 1 i p n ⋅ C [ i + 1 − n ] 1 − p i dp[i][1] = \frac{C[1]+\sum_{n=1}^{i}p^n\cdot C[i+1-n]}{1-p^i} dp[i][1]=1piC[1]+n=1ipnC[i+1n]
然后通过迭代把dp[ i ][ j∈[1,i ] ]都递推出来

注意特判一下p4<eps的情况

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
static const int maxn = 2000;
static const double eps = 1e-5;
double p1,p2,p3,p4;
int n,m,k;
double dp[maxn][maxn];
double C[maxn];
int main(){
    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 p = p2/(1-p1);
        double p31 = p3/(1-p1);
        double p41 = p4/(1-p1);
        //dp[1][1] = p41/(1-p);
        for(int i=1/*2*/;i<=n;i++){
            C[1] = p41;
            for(int j=2;j<=k&&j<=i;j++)
                C[j] = p31 * dp[i-1][j-1] + p41;
            for(int j=k+1;j<=i;j++)
                C[j] = p31 * dp[i-1][j-1];
            double tmp = C[1];
            for(int j=2;j<=i;j++)
                tmp+=C[j]*pow(p,i+1-j);
            dp[i][1] = tmp/(1-pow(p,i));
            for(int j=2;j<=i;j++)
                dp[i][j] = p * dp[i][j-1] + C[j];
        }
        printf("%.5f\n",dp[n][m]);
    }
    return 0;
}

数组再大一点可能就会爆M 可以用滚动数组来代替

参考链接:[https://www.cnblogs.com/kuangbin/archive/2012/10/03/2710987.html]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值