概率DP学习笔记(一)

来源:互联网 发布:管家婆软件客户端 编辑:程序博客网 时间:2024/05/22 16:58

  今天学了一下概率DP,做了几个题,记录一下。

 HDU4405

  这个题目的意思就是,棋盘由0到N构成,每次投骰子能投1-6,如果位置大于等于N,那么就算是到了终点。此外有一些飞行点,到了这个点就会直接到所对应的飞行点,并且可以多次飞行,即如果a是飞到b的飞行点,b又是飞到c的飞行点,那么就可以认为到a点的时候直接飞到了c点。问题是给你一个棋盘大小和飞行点,问你结束游戏所需回合的期望值。
  首先这是一个求期望的,所以就用倒推就好,但是记得开始前用dfs处理一下飞行点的问题,因为存在多次飞行的情况不能直接来。

  代码如下

#include <bits/stdc++.h>#define N 100100#define INF 0x3f3f3f3f#define LL long long#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double changce[N];map<int,int> mp,fin;void dfs(int beg,int en){    if(mp[en]==0){        fin[beg]=en;    }else{        dfs(beg,mp[en]);    }}void init(){    for(map<int,int>::iterator a=mp.begin(),b=mp.end();a!=b;++a){        dfs(a->first,a->second);    }}int main(){    ios::sync_with_stdio(false);    int n,m,temp1,temp2;    while(cin>>n>>m&&(n+m)){        mp.clear();        fin.clear();        while(m--){            cin>>temp1>>temp2;            mp[temp1]=temp2;        }        init();        for(int i=1;i<6;++i){            changce[n+i]=0;        }        changce[n]=0;        for(int i=n-1;i>=0;--i){            changce[i]=1;            for(int j=1;j<=6;++j){                if(i+j<=n){                    if(fin[i+j]==0){                        changce[i]+=changce[i+j]/6;                    }else{                        changce[i]+=changce[fin[i+j]]/6;                    }                }            }        }        cout<<fixed<<setprecision(4)<<changce[0]<<endl;    }    return 0;}

POJ2096

一个软件有s个子系统,会产生n种bug

每天发现一个bug,这个bug属于一个子系统,属于一个分类。

每个bug属于某个子系统的概率是1/s,属于某种分类的概率是1/n。

问发现n种bug且每个子系统都发现bug所需天数的期望。

是考虑系统、分类数而不考虑具体的是哪种分类,这个问题的抽象就很容易解决了

下面分析出自kuangbin博客

dp[i][j]表示已经找到i种bug,j个系统的bug,达到目标状态的天数的期望
dp[n][s]=0;要求的答案是dp[0][0];
dp[i][j]可以转化成以下四种状态:
 dp[i][j],发现一个bug属于已经有的i个分类和j个系统。概率为(i/n)*(j/s);
 dp[i][j+1],发现一个bug属于已有的分类,不属于已有的系统.概率为 (i/n)*(1-j/s);
 dp[i+1][j],发现一个bug属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(j/s);
 dp[i+1][j+1],发现一个bug不属于已有的系统,不属于已有的分类,概率为 (1-i/n)*(1-j/s);

#include <iostream>#include <iomanip>#define N 1010#define INF 0x3f3f3f3f#define LL long long#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double dp[N][N];int main(){    ios::sync_with_stdio(false);    int s,n;    while(cin>>n>>s){        dp[n][s]=0;        for(int i=n;i>=0;--i){            for(int j=s;j>=0;--j){                if(i==n&&j==s){                    continue;                }                dp[i][j]=(i*(s-j)*dp[i][j+1]+                         (n-i)*j*dp[i+1][j]+                         (n-i)*(s-j)*dp[i+1][j+1]+n*s)/(n*s-i*j);            }        }        /*for(int i=0;i<=s;++i){            for(int j=0;j<=n;++j){                cout<<fixed<<setprecision(4)<<dp[i][j]<<' ';            }            cout<<endl;        }*/        cout<<fixed<<setprecision(4)<<dp[0][0]<<endl;    }    return 0;}

HDU3853

  一个r*c的地图,在每个格子上都有对应的m1、m2、m3,在每一块格子上,下一回合有m1的几率不动,m2的几率下移,m3的几率右移,问你从(1,1)到(r,c)的所需步数期望

  这个也很裸,但是有个坑点,见代码

#include <bits/stdc++.h>#define N 1010#define INF 0x3f3f3f3f#define LL long long#define eps 1e-9#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double mp[N][N][3];double dp[N][N];int main(){    //ios::sync_with_stdio(false);    int r,c;    while(cin>>r>>c){        dp[r-1][c-1]=0;         for(int i=0;i<r;++i){            for(int j=0;j<c;++j){                scanf("%lf%lf%lf",mp[i][j],mp[i][j]+1,mp[i][j]+2);//这里使用cin会WA,不明白为什么            }        }        for(int i=r-1;i>=0;--i){            for(int j=c-1;j>=0;--j){                if(i==r-1&&j==c-1){                    continue;                }if(fabs(mp[i][j][0]-1.00)<1e-8)                    continue;                dp[i][j]=(dp[i+1][j]*mp[i][j][2]+dp[i][j+1]*mp[i][j][1]+2)/(1-mp[i][j][0]);            }        }        /*for(int i=0;i<r;++i){            for(int j=0;j<c;++j){                cout<<fixed<<setprecision(3)<<dp[i][j]<<' ';            }            cout<<endl;        }*/        cout<<fixed<<setprecision(3)<<dp[0][0]<<endl;    }    return 0;}

CSU1725

  这个题严格说来不是概率DP而是记忆化搜索,不过是顺便练了就凑进来了

  中文题目,所以就不解释题意了,直接上代码吧

#include <bits/stdc++.h>#define N 25#define INF 0x3f3f3f3f#define LL long long#define eps 1e-9#define mem(a,n) memset(a,n,sizeof(a))#define fread freopen("in.txt","r",stdin)#define fwrite freopen("out.txt","w",stdout)using namespace std;double dp[N][N][8][8][8];void init();double dfs(int x,int y,int a,int b,int c);int main(){        ios::sync_with_stdio(false);        int t,x,y,a,b,c;        cin>>t;        while(t--){            cin>>x>>y>>a;            init();            cout<<fixed<<setprecision(6)<<dfs(x,y,a,0,0)<<endl;        }        return 0;}void init(){    for(int i=0;i<N;++i){        for(int j=0;j<N;++j){            for(int k=0;k<8;++k){                for(int l=0;l<8;++l){                    for(int o=0;o<8;++o){                        dp[i][j][k][l][o]=-1;                    }                }            }        }    }}double dfs(int x,int y,int a,int b,int c){    if(dp[x][y][a][b][c]!=-1){        return dp[x][y][a][b][c];    }    if(y==0){        return dp[x][y][a][b][c]=1.0;    }    if(x<y||!x){        dp[x][y][a][b][c]=0;    }else{        dp[x][y][a][b][c]=1.0/(a+b+c+1)*dfs(x-1,y-1,a,b,c);        if(a+b+c<7){            if(a>0){                dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a,b+1,c);            }            if(b>0){                dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a+1,b-1,c+1);            }        }else{            if(a>0){                dp[x][y][a][b][c]+=1.0*a/(a+b+c+1)*dfs(x-1,y,a-1,b+1,c);            }            if(b>0){                dp[x][y][a][b][c]+=1.0*b/(a+b+c+1)*dfs(x-1,y,a,b-1,c+1);            }        }        if(c>0){            dp[x][y][a][b][c]+=1.0*c/(a+b+c+1)*(x-1,y,a,b,c-1);        }    }    return dp[x][y][a][b][c];}

关于为什么一般期望倒推,概率顺推的思考(一)

  我一开始猜想是不是因为类似0-1背包要从二维优化到一维的时候一样,要保证考虑的子问题的解是不会重复迭代的,拿0-1背包举例

//错误的代码for(int i=cost;i<volume;++i){    dp[i]=max(dp[i-cost]+value,dp[i]);}//正确的代码for(int i=volume;i>=cost;--i){    dp[i]=max(dp[i-cost]+value,dp[i]);}

  看代码可以直接想到,如果用前一个代码,那么在这一轮循环中可能出现一个物品拿了多次的情况,这就是被重复迭代了的结果,那么我们类似地通过代码考虑一下期望顺推的情况,这里以第一题为例。

  但是我写了一下代码……

  并不是这样的……

  然后问了一下学长,如果是期望的情况,推导的就是由终了状态到起始状态的逆推,而终了状态到终了状态的期望是0,建立在这样的基础上逆推就是正确的答案。

  而概率的话,终了状态的信息位置,无法逆推。

​  之后再补完,感觉还是没有完全弄懂。

原创粉丝点击