概率DP入門題

bigbigship發表於2014-10-20

概率問題的論文

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  (中等題) 

高斯消元,這題是學長出的,目的是坑人題意的, 題意理解以後就是很裸的高斯消元(用程式解方程),但還是有很多坑 


只做了一部分題目,剩下的以後做了會繼續更新。。。

相關文章