【概率DP】hdu 4089

注意,以下可能会多次吐槽这道题,前方高能。
另:感谢http://blog.csdn.net/morgan_xww/article/details/6920236提供的思路。


题目:http://acm.hdu.edu.cn/showproblem.php?pid=4089
一道2011年北京区域赛的原题。

题目大意:
貌似是玩一个不知道什么游戏,需要登录。登录的时候需要排队,会出现以下情况。
1号情况,登录失败,但是不影响排队顺序,概率为p1,
2号情况,登录失败,且影响排队顺序,从队头直接变成队尾,概率为p2
3号情况,登录成功,进入游戏,概率为p3,
4号情况,系统崩溃,全部玩家掉线,概率为p4,
现在问你,当排队人数为n人时,你的开始位置在第m位,问,你在k位以内的时候,系统崩溃的概率是多少。

思路:
首先由上面很容易推出状态转移方程。首先我们要想清楚我们要记录的状态,当队伍人数为i人时,玩家你在j号位。
那么就有以下情况。
当在第1位时,dp[i][1] = dp[i][1] * p1 + dp[i][i] * p2 + p4
当这一次在第一位的时候,有可能发生1号情况,下次还在第一位,有可能发生2号情况,下一次掉到了最后一位,有可能下一次直接系统崩溃,当然也有可能进入游戏,但是当i个人排队,该玩家已经进入游戏了,那么他在前k系统崩溃的概率为0。所以方程如上
当在2 <= j <= k时,dp[i][j] = dp[i][j] * p1 + dp[i][j - 1] * p2 + dp[i - 1][j - 1] * p3 + p4
当在j > k时,dp[i][j] = dp[i][j] * p1 + dp[i][j - 1] * p2 + p4

然后看到这样的式子,不妨直接可以计算时间复杂度为O(n^2),所求的概率为dp[n][m]
当然,dp[i][j]这东西,在转移的时候还在运算。。所以应该适当的化简一下
令:
p21 = p2 / (1 - p1)
p31 = p3 / (1 - p1)
p41 = p4 / (1 - p1)

那么,把等式右边的dp[i][j]化简一下
就有:
dp[i][1] = dp[i][i] * p21 + p41 (j == 1)
dp[i][j] = dp[i][j - 1] * p21 + dp[i - 1][j - 1] * p31 + p41 (2 <= j <= k)
dp[i][j] = dp[i][j - 1] * p21 + p41 (j > k)

直到这里,大家有没有一种想直接敲出来的感受呢。。。但是当我写到这的时候,我一直是瞅着题解的。因为自己dp能力菜,需要多学习。。但是,他们却说这会TLE。。难道有可以优化到O(nlogn)或者O(n)的算法?瞅了瞅他们代码,好像并不。。那时间怎么算起来都是O(n^2)。。但是就是TLE与AC的区别。。

辣么我们再来优化下吧。
当计算dp[i][j]时,dp[i - 1][j - 1]一定是一个常数,因为状态已经到不了那边了,辣么我们可不可以把dp[i][j]的第j项的常数,记成c[j]。那么方程如下:

dp[i][1] = dp[i][i] * p21 + p41 (j == 1)

dp[i][j] = dp[i][j - 1] * p21 + c[j]
c[j] = p41 + dp[i - 1][j - 1] * p31 (2 <= j <= k)

dp[i][j] = dp[i][j - 1] * p21 + c[j]
c[j] = p41 (j > k)

那是不是有:
dp[i][1] = dp[i][i] * p21 + p41
dp[i][2] = dp[i][1] * p21 + c[2]
dp[i][3] = dp[i][2] * p21 + c[3]
……
dp[i][i] = dp[i][i - 1] * p21 + c[i]
一个i元一次方程组
dp[i][1] = (dp[i][2] - c[2]) / p21
dp[i][2] = ((dp[i][3] - c[3]) / p21 - c[2]) / p21

一直迭代dp[i][i] * p21 ^ i
c[i] * p21 ^1,c[i - 1] * p21 ^ 2 …… c[2] * p21 ^ (i - 1) (该数求和得到temp)
pp为p21的i次方
再把这些值求和,解一下方程。得出,dp[i][i] = (temp + pp * p41) / (1 - pp * p21);
dp[i][1] = dp[i][i] * p21 + p41;

然后再把接带回原方程解出dp[i][2 -> i]
任务完成。了吗

小编我交这题,10发MLE是白来的吗。
万脸懵逼。跟楼上的那个题解一样的内存,MLE!???然后就开始优化,各种减少使用内存,然后发现,减少了之后,跑出来的内存更大了。。。然后还不信邪,换着编译器交,一样MLE,最终把楼上博客的代码丢进去,一起MLE。。。我的天喵喵喵??
然后开始怀疑题目。。又换了个题解,把代码一丢进去,A了。看了看内存,噢,我比他多个数组,然后又优化了下,样例过了,然后丢上去,接着MLE,内存比原本多开个数组还要大。。。辣鸡hdu毁我一生。接着改,改到真的不知道改哪里才能把内存所小了,就开始改define部分,后来又发现pritnf那,我是%lf他是%f,他可以过。不会MLE就是这里吧,然后又跟着改,依旧MLE,途中还怀疑过,是不是return 0没写。。补上了个return 0交了一发,依旧MLE。神奇的MLE。。
最最最后,我一行一行代码对着看,终于发现了一处,可能处理不恰当会增大运行时间的,你没看错,处理不恰当会增大运行时间,而且在我看来影响也不会很大。。特判了下如果大于k就不运算了。一交AC。
(喵喵喵)^n,这可能会TLE的东西,跟MLE有半毛钱关系啊,难道TLE和MLE就是雷锋和雷锋塔的关系?这个MLE搞了接近一个钟。人生都怀疑了n次了。。

题目虽然说营养很高。但是这个MLE。。真的是让我万份懵逼。
虽然TLE真的是感觉非常不合理,但是你MLE合理之处又何在呢。
真的是。这种题。感觉吐槽100次还不够。。。


习惯性附上小编的代码

/*
@resources: hdu 4089
@date: 2017-3-12
@author: QuanQqqqq
@algorithm: 概率dp 
*/
#include<bits/stdc++.h>

#define eps 1e-10
#define MAXN 2005
#define zero(a) (fabs(a) < eps)

using namespace std;

double c[MAXN],dp[MAXN][MAXN];
double p1,p2,p3,p4,p21,p31,p41,temp,pp;
int n,m,k;
int i,j;

int main(){
    while(~scanf("%d %d %d %lf %lf %lf %lf",&n,&m,&k,&p1,&p2,&p3,&p4)){
        if(zero(p4)){
            printf("0.00000\n");
            continue;
        }
        p21 = p2 / (1 - p1);
        p31 = p3 / (1 - p1);
        p41 = p4 / (1 - p1);
        dp[1][1] = p4 / (1 - p1 - p2);
        for(i = 2;i <= n;i++){
            for(j = 2;j <= k && j <= i;j++){
                c[j] = dp[i - 1][j - 1] * p31 + p41;
            }
            for(j = k + 1;j <= i;j++){
                c[j] = dp[i - 1][j - 1] * p31;
            }
            temp = 0,pp = 1;
            for(j = i;j > 1;j--){
                temp += pp * c[j];
                pp *= p21;
            }
            dp[i][i] = (temp + pp * p41) / (1 - pp * p21);
            dp[i][1] = dp[i][i] * p21 + p41;
            for(j = 2;j < i;j++){
                dp[i][j] = dp[i][j - 1] * p21 + c[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、付费专栏及课程。

余额充值