【概率DP】hdu 4089

来源:互联网 发布:视图mysql 编辑:程序博客网 时间:2024/06/03 19:03

注意,以下可能会多次吐槽这道题,前方高能。
另:感谢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 0
原创粉丝点击