首页 > 代码库 > 概率DP入门题

概率DP入门题

概率问题的论文

1.算法合集之《信息学竞赛中概率问题求解初探》

2.有关概率和期望问题的研究

3.算法合集之《浅析竞赛中一类数学期望问题的解决方法》


二 入门题目

1、POJ 3744 Scout YYF I (简单题) 
题意:一条路上有n个地雷 ,a[i]代表第i个地雷放的位置,求安全走过这段路的概率

分析:若第k个位置有地雷则安全走过这个位置的方案为在第k-1个位置跳两步概率为(1-p)

从反面考虑 已经安全走过了第i-1个雷 则在第i个雷的死掉的概率为 1-p(从走到a[i-1]走到a[i])  P=p*ans[i-1]+(1-p)*ans[i-2]  

ans[i]代表走到i位置的概率,由于n比较大 需要用矩阵进行优化
解题报告:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

int a[20];

struct matrix
{
    double m[2][2];
};

matrix I={1.0,0,0,1.0};

matrix multi(matrix A,matrix B)
{
    matrix C;
    for(int i=0;i<2;i++){
        for(int j=0;j<2;j++){
            C.m[i][j]=0;
            for(int k=0;k<2;k++)
                C.m[i][j]+=A.m[i][k]*B.m[k][j];
        }
    }
    return C;
}
double pow(int k,matrix A)
{
    matrix ans = I,p=A;
    while(k){
        if(k&1){
            ans=multi(ans,p);
            k--;
        }
        k>>=1;
        p=multi(p,p);
    }
    return ans.m[0][0];
}

int main()
{
    int n;
    double p;
    while(cin>>n>>p){
        for(int i=1;i<=n;i++)
            cin>>a[i];
        a[0]=0;
        sort(a,a+n+1);
        matrix A={p,1.0-p,1.0,0};
        double ans=1;
        for(int i=0;i<n;i++){
            ans*=(1-pow(a[i+1]-a[i]-1,A));
        }
        printf("%.7lf\n",ans);
    }
    return 0;
}


 2、POJ 3071 Football      (简单题) 
足球赛的淘汰赛问题。问最后胜利的概率最大的球队。每个队的胜率都用dp算一下比较 
解题报告 


3、codeforces 148D Bag of mice   (简单题) 
抓老鼠问题。记忆化搜索的话不是很难,要写成for循环的那就比较麻烦了 
解题报告         

 
 4、POJ 2151 Check the difficulty of problems   (简单题) 
算很常见的一种dp表示吧。 学会 “至少” 这类问题角度的转化。 
解题报告  


5、ZOJ  3380 Patchouli‘s Spell Cards (中等题) 
有m个位置,每个位置填入1~n中的一个数,求至少有L个数一样的概率。 dp转移还行,就是要用Java的     高精度                   解题报告  


6、SGU  495 Kids and Prizes     (YY题) 
 YY题,O(1)推出结论,想不到就觉得比较难, 分析问题的角度值得学习。 
解题报告 


7、POJ 2096 Collecting Bugs   (简单题) 
题意:一个人一天只能找出一个bug来 一个公司有s个子公司 ,有n种bug,让这个人去找,求找出所有的n种bug而且每个公司都要找出bug所需要的时间的期望

分析:dp求期望入门题。关键要体会为什么要倒着推过来  
dp[i][j]表示在i个公司中一共找到了j种bug

注:E(aA+bB+cC+.....n*N)=a*E(A)+b*E(B)+.......+n*E(N); 

状态转移方程很好理解

dp[i][j]=(dp[i+1][j]*p1+dp[i][j+1]*p2+dp[i+1][j+1]+1)/(1-p4);

p1=(s-i)*j/n/s;

p2=i*(n-j)/n/s;

p3=(s-i)*(n-j)/n/s;

p4=i*j/n/s;

解题报告 

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 1010;

double dp[maxn][maxn];

int main()
{
    int n,s;
    while(~scanf("%d%d",&n,&s)){
        dp[s][n]=0;
        for(int i=s;i>=0;i--){
            for(int j=n;j>=0;j--){
                if(i==s&&j==n)
                continue;
                dp[i][j]=(dp[i+1][j]*(s-i)*j*1.0/(s*n*1.0)+dp[i+1][j+1]*(s-i)*(n-j)*1.0/(s*n*1.0)+dp[i][j+1]*i*(n-j)*1.0/(s*n*1.0)+1.0)/(1.0-i*j*1.0/n/s);
            }
        }
        printf("%.4lf\n",dp[0][0]);
    }
    return 0;
}




 8、HDU 3853 LOOPS      (简单题) 
 同第6题, 但要注意一个小问题 

题意:一个r*c的地图 一个人在(1,1)每次可以留在原地 或者消耗两个体力向左走一格或则向下走一格,求走到(r,c)所消耗的体力的期望

分析:dp[i][j]表示从(i,j)走到终点所消耗体力的期望

dp[i][j]=(dp[i+1][j]*p1+dp[i][j+1]*p2+2)/(1-p3)

p1为向右走的概率 ,p2为向下走的概率 ,p3为原地不动的概率

注:E(aA+bB+cC+.....n*N)=a*E(A)+b*E(B)+.......+n*E(N); 包含状态A,B,C的期望可以分解子期望求解

解题报告

#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 1002;

double map[maxn][maxn][3];
double dp[maxn][maxn];

int main()
{
    int r,c;
    while(~scanf("%d%d",&r,&c)){
        for(int i=1;i<=r;i++)
            for(int j=1;j<=c;j++)
            scanf("%lf%lf%lf",&map[i][j][0],&map[i][j][1],&map[i][j][2]);
        memset(dp,0,sizeof(dp));
        for(int i=r;i>=1;i--){
            for(int j=c;j>=1;j--){
                if(i==r&&j==c)
                    continue;
                if(map[i][j][0]==1.00)
                    continue;
                dp[i][j]=(map[i][j][1]*dp[i][j+1]+map[i][j][2]*dp[i+1][j]+2)/(1-map[i][j][0]);
            }
        }
        printf("%.3lf\n",dp[1][1]);
    }
    return 0;
}


9、HDU  4405 Aeroplane chess   (简单题) 
这题是2012年网络赛的题目。同第6题,只是题目多加了一个小小的条件 

分析:dp[i]=1/6(dp[i+1]+....+dp[i+6]+1)   如果i可以直接到k的话 则dp[i]=dp[k]; dp[i]表示从i到终点所要扔的色子次数的期望
解题报告 

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int maxn = 100007;

double dp[maxn];

int f[1010][2];

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        if(n==0&&m==0)
        break;
        for(int i=0;i<m;i++)
            scanf("%d%d",&f[i][0],&f[i][1]);
        memset(dp,0,sizeof(dp));
        double p = 1.0/6;
        for(int i=n;i>=0;i--){
            if(i>=n) continue;
            dp[i]=(dp[i+1]+dp[i+2]+dp[i+3]+dp[i+4]+dp[i+5]+dp[i+6])*p+1;
            for(int j=0;j<m;j++){
                if(i==f[j][0]){
                    dp[i]=dp[f[j][1]];
                    break;
                }
            }
        }
        printf("%.4lf\n",dp[0]);
    }
    return 0;
}


10、ZOJ 3640  Help Me Escape    (简单题) 
做完6,7,8题后仔细想想,还是差不多的题, 记忆化搜索也容易想到 
解题报告 

#include<cstdio>  
#include<cstring>  
#include<cmath>  
#define max(a,b) a>b?a:b  
const int LMT=20003;  
const double is=0.5*(1+sqrt(5));  
double dp[LMT];  
int have[102];  
int main()  
{  
    int i,n,f,j,all;  
    while(~scanf("%d%d",&n,&f))  
    {  
        memset(dp,0,sizeof(dp));  
        all=-1;  
        for(i=0;i<n;i++)  
        {  
            scanf("%d",&have[i]);  
            all=max(all,have[i]);  
        }  
        all<<=1;  
        for(i=max(all,f);i>=f;i--)  
        {  
            for(j=0;j<n;j++)  
                if(i>have[j])dp[i]+=(int)(is*have[j]*have[j]);  
                else dp[i]+=1+dp[i+have[j]];  
                    dp[i]/=n;  
        }  
        printf("%.3lf\n",dp[f]);  
    }  
    return 0;  
}



11、ZOJ 3822 Domination (简单题) 

题意:给定一个r*c的棋盘 每一步可以放一个棋子 求多少步可以使每行每列都有棋子的期望

分析:这题是2014年 2014 ACM-ICPC Asia Mudanjiang Regional 的题目,其实只要做过 概率dp的题目就很好写的

dp[i][j][k]表示一共用了i个棋子 使i行k列有了棋子

dp[i][j][k]=(dp[i+1][j][k]*p1+dp[i+1][j+1][k]*p2+dp[i+1][j][k+1]*p3+dp[i+1][j+1][k+1]*p4)

p1=(j*k-i)/(r*c-i);
p2= (r-j)*k/(r*c-i);

p3=(c-k)*j/(r*c-i);

p4=(r-j)*(c-k)*(r*c-i);

解题报告:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int maxn = 51;

double dp[maxn*maxn][maxn][maxn];

int main()
{
    int r,c,t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&r,&c);
        memset(dp,0,sizeof(dp));
        for(int i=r*c-1;i>=0;i--){
            for(int j=r;j>=0;j--){
                for(int k=c;k>=0;k--){
                    if(j==r&&k==c) continue;//判断分母是不是为0
                    if(j*k<i) continue;//判断分子是不是负数
                    double p1=1.0*(j*k-i)/(r*c-i);
                    double p2=1.0*(r-j)*k/(r*c-i);
                    double p3=1.0*(c-k)*j/(r*c-i);
                    double p4=1.0*(r-j)*(c-k)/(r*c-i);
                    //  cout<<"p1 "<<p1<<endl;
                    // cout<<"p2 "<<p2<<endl;
                    //cout<<"p3 "<<p3<<endl;
                    //cout<<"p4 "<<p4<<endl;
                    dp[i][j][k] =dp[i+1][j][k]*p1+dp[i+1][j+1][k]*p2+dp[i+1][j][k+1]*p3+dp[i+1][j+1][k+1]*p4+1.0;
                }
            }
        }

        printf("%.8lf\n",dp[0][0][0]);
    }
return 0;
}


12、HDU 4336 Card Collector  (简单题) 
 状态压缩概率DP。或者用容斥原理直接求。 
题意:有n种卡片,每买一包零食则可能得到其中的一种也可能得不到,得到第i种卡片的概率为pi求,收集齐所有的卡片需要的买零食的期望

由于n比较小我们可以进行状态压缩

dp[i] 化成二进制1的位置代表收集了 这几种所需要买的零食的期望

eg: dp[(1101)2] =(dp[(1100)2]*p1+dp[(1001)2]*p3+dp[(0101)2]*p4+1)/(1-p3-p(零食里没有卡片)) 

解题报告 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn = 1<<20;

double dp[maxn+1];
double p[25];

int main()
{
    int n;
    while(~scanf("%d",&n)){
        p[0]=1;
        for(int i=1;i<=n;i++){
            scanf("%lf",p+i);
            p[0]-=p[i];
        }
        dp[0]=0;
        double x;
        for(int i=1;i<(1<<n);i++){
            x=p[0];
            dp[i]=1;
            for(int j=0;j<n;j++){
                if((1<<j)&i) dp[i]+=p[j+1]*(dp[i-(1<<j)]);
                else x+=p[j+1];
            }
            dp[i]/=(1-x);
        }
        printf("%.4lf\n",dp[(1<<n)-1]);
    }
    return 0;
}



13、ZOJ 3329  One Person Game   (中等题-) 
骰子求期望的题, 递推公式有点麻烦。 
解题报告 


14、hdu 4652 Dice     (中等题-) 
dp求期望 推数学公式, 最近第5场多校题, 赛后发现其实是道很基础的题,可惜没学过, 用高中知识就能推出来了 
解题报告 
 

15、HDU 4035 Maze     (中等题+) 
树上的dp递推, 写出递推关系方程组后,用dfs从树的叶子节点一路往上迭代方程求解 
解题报告  


16、HDU 4089 Activation    (中等题) 

2011年北京现场赛的题目, 用求期望的方法 求概率的题,公式有一点点繁琐,仔细想也是可以做出来的                     、

解题报告  



17、HDU 4418 Time travel  (中等题) 

高斯消元,这题是学长出的,目的是坑人题意的, 题意理解以后就是很裸的高斯消元(用程序解方程),但还是有很多坑 


只做了一部分题目,剩下的以后做了会继续更新。。。

概率DP入门题