概率与期望DP小结。

  好久没更新博客,来水一蛤,近三天做了二十道概率题,也算是摸到了一点门道。

首先关于一个很多博主的说到的点,也是我个人认为很重要的一个点,就是概率要顺着推,期望倒着推,我比较赞同以下这篇博客的观点:https://blog.csdn.net/nameofcsdn/article/details/52082746

其次就是关于概率DP状态的设计,概率DP的题往往设计状态比较直观,直接按照题意来就可以(也有可能是我做的题太水了)。关于状态的转移,其最重要的核心就是严格按照全概率公式和全期望公式来递推,要保证枚举出所有可能的状态,并保证他们是互斥的事件并且概率和为1.下面是几个简单的例题。

概率:

1,codeforces 148D Bag of mice

题目链接:https://vjudge.net/problem/CodeForces-148D

题意:袋子里有w只白鼠,b只黑鼠,P先抓,D后抓,并且D抓老鼠时会随机跑出来一只老鼠,谁抓到白鼠谁获胜。先求P获胜的概率。

设dp[i][j]为P抓老鼠时,袋子里还剩i只白鼠,j只黑鼠这种情况发生的概率,显然初始状态是w只白鼠,b只黑鼠,所以dp[w][b]=1,然后从该状态出发,由于我们设定的dp数组是P抓老鼠时所面临的情形,所以状态转移时要往前想两步。也就是dp[i][j]可能会由dp[i+1][j+2]和dp[i][j+3]两种状态转移过来,注意这里要想清楚,由于我们的dp数组保存的时这种情形发生的情况,所以中途是不能出现有人获胜的,也就是状态转移的过程中P,D都只能抓到黑鼠。这样就可以得出状态转移方程:

 if (j+2<=b&&i+1<=w) dp[i][j]+=dp[i+1][j+2]*((j+2)*1.0/(i+j+3))*((j+1)*1.0/(i+j+2))*((i+1)*1.0/(i+j+1));
  if (j+3<=b) dp[i][j]+=dp[i][j+3]*((j+3)*1.0/(i+j+3))*((j+2)*1.0/(i+j+2))*((j+1)*1.0/(i+j+1));

然后对于所有的情形,你只需要让P接来下抓到白鼠就可获胜,对这些概率求和即可。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<bitset>
#include<algorithm>
using namespace std;
const long long MAXM=1e10+10;
const int INF=1e9;

int w,b;
double dp[1010][1010];

int main()
{
    scanf("%d%d",&w,&b);
    memset(dp,0,sizeof(dp));
    dp[w][b]=1;
    for (int i=w;i>=1;i--) {
        for (int j=b;j>=0;j--) {
            if (j+2<=b&&i+1<=w) dp[i][j]+=dp[i+1][j+2]*((j+2)*1.0/(i+j+3))*((j+1)*1.0/(i+j+2))*((i+1)*1.0/(i+j+1));
            if (j+3<=b) dp[i][j]+=dp[i][j+3]*((j+3)*1.0/(i+j+3))*((j+2)*1.0/(i+j+2))*((j+1)*1.0/(i+j+1));
        }
    }
    double ans=0;
    for (int i=1;i<=w;i++) {
        for (int j=0;j<=b;j++)
            ans+=dp[i][j]*(i*1.0)/(i+j);
    }
    printf("%.9lf\n",ans);
    return 0;
}

2,HDU 3366 Passage

题目链接:https://vjudge.net/problem/HDU-3366

题意:Bill面前有n扇门,身上有m块钱,Bill选择一扇门,Bill有p的概率走出去,q的概率遇到卫兵,如果遇到卫兵,你需要花费1块钱才能活着回到迷宫,然后剩下1-p-q的概率遇到死路从而回到迷宫。求在最优策略下逃出迷宫的概率。

关于这题的最优策略其实是我一直没有理解的点,个人觉得逃出迷宫的总概率应该是一个与策略无关的定值,但是却是对于样例2而言的话,两种顺序的结果不一样,如果有人可以解释清楚的话希望可以留下评论。在按照的所谓的最优策略下,也就是按照p/q排序,优先选择p/q较大的门。然后定义dp[i][j]为出现在第i扇门身上还有j块钱的情形出现的概率。在这里说一下,对于概率dp的题,我的状态定义方式与大多数博主都不一样,我看许多博客都是直接定义dp数组为某种情况下获胜的概率,但是我个人认为这种定义方式并不好理解(也许是我智商堪忧),所以我都是将dp数组定义为某种情形出现的概率,这样方便理解和推导状态的转移。

由于这样定义状态之后,所以进行转移时是不考虑Bill逃出的情况,也不考虑中间死亡的情况(因为一旦出现这些情况,接下来的情形是不会出现的),显然初始状态是0扇门身上有m块钱,dp[0][m]=1,状态转移方程如下。

dp[i][j]+=dp[i-1][j]*(1-r[i-1].p-r[i-1].q);
if (j+1<=m) dp[i][j]+=dp[i-1][j+1]*r[i-1].q;

 得出所有情形的概率后,在每种情形下乘上在该种情形下逃出的概率,求和,即为逃出的总概率。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#include<vector>
#include<map>
#include<bitset>
#include<algorithm>
using namespace std;
const long long MAXM=1e5+10;
const int INF=1e9;

struct rd {
    double p,q;
    rd () {}
    rd (double pp,double qq) {
        p=pp; q=qq;
    }
};

int n,m;
rd r[1010];
double dp[1010][20];

bool  cmp(rd r1,rd r2) {
    return (r1.p/r1.q)>(r2.p/r2.q);
}
int main()
{
    int T;
    scanf("%d",&T);
    int cas=1;
    while(T--) {
        scanf("%d%d",&n,&m);
        double x,y;
        for (int i=1;i<=n;i++) {
            scanf("%lf%lf",&x,&y);
            rd r1(x,y); r[i]=r1;
        }
        sort(r+1,r+1+n,cmp);
        memset(dp,0,sizeof(dp));
        dp[0][m]=1;
        for (int i=1;i<=n;i++) {
            for (int j=m;j>=0;j--) {
                dp[i][j]+=dp[i-1][j]*(1-r[i-1].p-r[i-1].q);
                if (j+1<=m) dp[i][j]+=dp[i-1][j+1]*r[i-1].q;
            }
        }
        double ans=0;
        for (int i=1;i<=n;i++)
            for (int j=0;j<=m;j++)
            ans+=dp[i][j]*r[i].p;
        printf("Case %d: ",cas++);
        printf("%.5lf\n",ans);
    }
    return 0;
}

 

以上就是关于概率的内容,下篇博客会更新一些关于期望的题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值