概率DP

概率dp顾名思义就是求一件事件发生的概率。那么和其他的dp有什么区别?没有实际上的区别,只是转移的是概率这一维,而且基本上概率这一位只能装在dp值里面(其他很多dp可以把要的答案放到下标里的)。对于概率dp完全可以把他当做普通的dp,只是有一维固定了罢了。概率dp可以和很多类型挂钩,比如背包,树形,状压等等。

几道例题:

一、HDU 2955 Robberies
如果问的不是概率的话,很简单就能看出是一个01背包;如果换成概率的话也是一样的,01背包的本质不会改变。
转移部分:

for(int i=1;i<=n;i++)
    for(int j=All;j>=A[i];j--)
        dp[j]=max(dp[j],dp[j-A[i]]*(1-G[i]));

01背包的模型加上概率dp的转移

二、HDU 3076 ssworld VS DDD
同理,也可以很容易按照原来的思路定义dp[i][j] 表示A为i血B为j血时的概率。那么转移的时候也就很简单了,只要从上一层转移就行了。但是因为有平局的情况,平局就是一种特殊的情况,如果还是按照那个思路,岂不是dp[i][j]+=dp[i][j] *(平局的概率),显然这样下去很容易错掉。那怎么办?能不能把平局这个情况省略掉?只要假装没有平局的概率就行了。省略掉平局的概率,如果再按照原来的胜负概率,肯定也有问题。那么就改成Pwin=(Pwin)/(Pwin+Plose)。什么意思?如果只有胜和负,那么现在胜的概率就是胜除以(胜和负)的和。负同理。没有平局的特殊情况那就简单了,直接转移

for(int i=1;i<=H1;i++)
    for(int j=1;j<=H2;j++)
        dp[i%2][j]=(P1*dp[i%2][j-1]+P2*dp[1-i%2][j])/(1-P3);

这个情况在很多概率dp里都会有出现,就是某些特殊情况要把它当做没有,然后重新算一遍对应的概率。但是有些时候不能这么算,看下一道例题

三、HDU 3366 Passage
三种情况,逃出去,遇守卫,死胡同。转移的时候也分三条。还有一点,转移前先排序。这样逃出去的概率更大。

for(int i=1;i<=n;i++)
    for(int j=m;j>=0;j--){
        if(j>=1)dp[i+1][j-1]+=dp[i][j]*A[i].q;//守卫
        dp[i+1][j]+=dp[i][j]*(1-A[i].q-A[i].p);//死胡同
        ans+=dp[i][j]*A[i].p;//逃出去
    }

那么这道题能不能像上面那一道一样,把死胡同这个也给去掉,然后另两个概率重新计算呢?如果像上面这个程序这样写,肯定是不行的。为什么?因为我的定义不一样。上面装的是会碰到守卫的概率,而不是逃出去的概率,当然不行了。如果改成逃出去的概率呢?我想是可以的,因为跟上面那题是差不多的,我还没有尝试过。

四、HDU 3689 Infinite monkey theorem
dp也很好定义:dp[i][j] 表示巧了i下,匹配了j个的概率。那么转移也很简单了,只要多加一个后算出多一个字符后匹配的长度。算匹配的长度是这道题的重点。因为数据小,暴力m^3也行,kmp也行,AC自动机也行。概率在这道题没什么大体现。

五、HDU 4219 Randomization?
与第一题一样,概率dp和其他dp的结合。此题是树形dp。只要记录一下这个节点下的距离出现的概率就行了。转移也和树形dp的一样,dfs实现。细节就不说了。这个想法其实是从求树的直径的另一种方法衍生过来的。树的直径也就是求任意两点间距离的最大值,这题是求不大于S的概率,很像。

写概率dp时,多想想如果求得不是概率的话,有没有很类似的题,然后按照那个的想法想下来,可能就是正解了。

六、HUD 4487 Maximum Random Walk
这道题题意我自认为一直很有毒。最远的期望位置???就是随便顶一个位置,那就是期望位置了……其他的也没什么。dp两种写法,一种是下标加100,然后就可以直接转移了。一种是认为在原点左边就是在原点,其实效果是一样的,直接转移就行了。

for(int i=1;i<=n;i++)
    for(int j=0;j<=n;j++){
        if(j)dp[i][j]+=dp[i-1][j-1]*R,dp[i][j]+=dp[i-1][j]*(1-L-R);//在右边时的两种情况
        else dp[i][j]+=dp[i-1][j]*(1-R);//在左边
        dp[i][j]+=dp[i-1][j+1]*L;//向左走
    }

七、HDU 4576 Robot
这题没什么好讲的,只是卡卡常就过了。

八、HDU 4800 Josephina and RPG
题目歧义也很多。给出的m是没有用的,有用的是C(m,3),要自己算,这才是真的队伍数量。其实如果不考虑答应了可以交换这一点,就只是对每一种队伍一遍循环过去求一下。那么如果可以交换呢?其实是一样的,只要把交换后的队伍里更新一下(不是累加)获胜的概率就行了。

for(int i=1;i<=n1;i++){
    cur^=1;//滚动
    int x;
    scanf("%d",&x);
    dp[cur][x]=0;
    for(int j=0;j<n;j++){
        if(j!=x)dp[cur][j]=dp[1-cur][j]*G[j][x];//不一样的话就转过去
        dp[cur][x]=max(dp[cur][x],dp[1-cur][j]*G[j][x]);//问的是最有的,取max
    }
}

这道题是取最大值,而不是累加概率的和。具体题目具体看,不要一直在累加、累乘(虽然很多题都是累加的)

九、HDU 5001 Walk
开始时把题目想得太复杂了,然后怎么想都有问题。其实不用想很多,只要我那个前一步上把和此点相连的点转移过来就行了(填表法)。而且我的写法很暴力,暴力到无解(15s的限时)

for(int i=1;i<=n;i++){
    memset(dp,0,sizeof dp);
    for(int j=1;j<=n;j++)if(j!=i)dp[0][j]=1.0/n;
    double ans=0;
    for(int j=1;j<=d;j++){
        for(int k=1;k<=n;k++){
            if(k==i)continue;
            for(int l=0;l<(int)edge[k].size();l++)
                dp[j][k]+=dp[j-1][edge[k][l]]/(int)edge[k].size();
        }
    }

十、HDU 5602 Black Jack
反正到现在,我还是有点蒙的。看完了题解后,有一点点懂。主要是如何理解题目中给出的(两个人都很聪明)这一点。
直接上解析
D[i][j]表示现在是闲家叫牌,闲家点数i、庄家点数j,闲家赢的概率
D1[i][j]表示现在轮到庄家叫牌,闲家点数i、庄家点数j,庄家赢的概率

    for(int i=21;i>=2;i--)
        for(int j=21;j>=2;j--){
            if(i<=j){G1[i][j]=1;continue;}
            for(int k=1;k<=13;k++)G1[i][j]+=G1[i][j+A[k]]/13;
        }
    for(int i=21;i>=2;i--)
        for(int j=21;j>=2;j--){
            for(int k=1;k<=13;k++)G[i][j]+=G[i+A[k]][j]/13;
            if(1-G1[i][j]>G[i][j])G[i][j]=1-G1[i][j];
        }

这是预处理的一段。
字面意思:从后往前推。只有后面的情况推出来,才能推后面的。
然后每次把多一个分数的概率加上去就行了。
答案就先把初始分数a、b算出来,然后当G[a][b]>0.5就输出YES

十一、HDU 5816 Hearthstone
一种方法显然是用状压,然后去算。
另一种是用深搜,同样的计算方法。
只是这道题和其他的不一样的是不是直接求概率,因为他的输出是最简分数,那么就要求出方案数,最后求一遍gcd。那么完全可以不把它当做概率dp来写。

总结一下:
1、十一道概率dp,有点小懂。所谓概率dp,很多是在其他常见的dp类型上建立的,仅仅是存的值是概率。如果看成普通的dp来写,其实也是差不多的。唯一一个限制条件就是存的值是概率,这个固定了,其他的都是一样的。

2、一些概率dp(如第二、三两题)的给出的概率是可以转化成其他样式,从而简化dp的转移。

3、概率dp不是都是累加的,也可能是最大值。

4、思考概率dp,可以先把概率改成求方案数,或者求另一个东西,然后在想,可能可以找到一种正确的方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值