HDU 4089 Activation (概率DP )

参考1  参考2

题目:仙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]);
    }
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值