概率dp 入门

来源:互联网 发布:速记用什么软件 编辑:程序博客网 时间:2024/05/18 19:42

有dp就一定有状态和状态转移,不同的就是概率dp处理的是概率或者期望

不知道该说点什么,就贴贴代码了。

A - Scout YYF I

经典的跳地雷的题目,有p的概率往前走一步,有1-p的概率跳两格,求安全越过所有地雷的概率,这题由于数据量较大,推出公式后用矩阵快速幂做,poj的G++printf需要用%f才能过。。。

#include<cstdio>#include<string.h>#include<algorithm>#include<iostream>using namespace std;typedef long long ll;const int maxn = 105;const int maxe=300;const int inf = 0x3f3f3f3f;const int mod=1000000007;int n,k,m;double p;double dp[maxn];int place[maxn];struct matrix{    double ju[2][2];    matrix(){memset(ju,0,sizeof(ju));}    matrix operator *(matrix b){        matrix cnt;        for(int i=0;i<2;i++){            for(int j=0;j<2;j++){                for(int k=0;k<2;k++){                    cnt.ju[i][j]+=ju[i][k]*b.ju[k][j];                }            }        }        return cnt;    }    void operator=(matrix b){        for(int i=0;i<2;i++){            for(int j=0;j<2;j++)ju[i][j]=b.ju[i][j];        }    }};double pow_mod(double a,double b,int c){    matrix cc;    cc.ju[0][0]=p,cc.ju[0][1]=1-p;    cc.ju[1][0]=1;cc.ju[1][1]=0;    matrix ans;    ans.ju[0][0]=1;ans.ju[0][1]=0;    ans.ju[1][0]=0;ans.ju[1][1]=1;    int kkk=1;    while(c){        if(c%2)ans=ans*cc;        c/=2;        cc=cc*cc;    }    return a*ans.ju[0][0]+b*ans.ju[0][1];}int main(){    while(cin>>n>>p){        memset(dp,0,sizeof(dp));        for(int i=1;i<=n;i++){           cin>>place[i];        }        sort(place+1,place+1+n);        if(place[1]==0)dp[0]=0;        else dp[0]=1;        int cnt=1;        for(int i=1;i<=n;i++){            double kk;            int step=place[i]-1-cnt;            if(step<0){dp[i]=0;break;}            if(step==0){kk=dp[i-1];}            else if(step==1){kk=dp[i-1]*p;}            else{                kk=pow_mod(dp[i-1]*p,dp[i-1],step-1);            }            dp[i]=kk*(1-p);            cnt=place[i]+1;        }            printf("%.7f\n",dp[n]);    }    return 0;}

B - Collecting Bugs

找bug,有n种bug和m个子系统,每天可以找到一个bug,但是bug的种类和所处于子系统的概率都是均等的,求找到所有种类的bug以及在所有子系统种都找到bug的期望时间。
注意边界处理

#include <string.h>#include<cstdio>using namespace std;const int maxn=1005;int n,m;double dp[maxn][maxn];bool vis[maxn][maxn];double DP(int a,int b){    if(vis[a][b])return dp[a][b];    double ans=0;    int k1=n-a,k2=m-b;    if(k1&&k2)    ans+=double(k1)/n*k2/m*(1+DP(a+1,b+1));    if(k1&&b)    ans+=double(k1)/n*double(b)/m*(1+DP(a+1,b));    if(k2&&a)    ans+=double(k2)/m*double(a)/n*(1+DP(a,b+1));    if(a&&b){        double p4=double(a)/n*b/m;        ans+=p4;        ans/=(1.0-p4);    }    vis[a][b]=1;    return dp[a][b]=ans;}int main(){    while(~scanf("%d%d",&n,&m)){        memset(dp,0,sizeof(dp));        memset(vis,0,sizeof(vis));        vis[n][m]=1;        printf("%.4f\n",DP(0,0));    }    return 0;}

C - One Person Game

摇骰子,若三个骰子的点数分别为a,b,c,则分数归零,否则分数+=a=b=c,分数大于等于n游戏结束,求游戏结束的期望时间

因为每一个状态都可能转移到分数为0的状态,所以不能像之前那样求了,得从公式中推出通式。

#include <string.h>#include<cstdio>using namespace std;const int maxn=605;const int maxm=35;int n,k1,k2,k3,a,b,c;double p0;double p[6*6*6+5];double A[maxn],B[maxn];int main(){   int t;   scanf("%d",&t);   while(t--){    memset(p,0,sizeof(p));    scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);    p0=1.0/k1/k2/k3;    for(int i=1;i<=k1;i++)        for(int j=1;j<=k2;j++)            for(int k=1;k<=k3;k++)                if(!(i==a&&j==b&&k==c))                    p[i+j+k]+=p0;    memset(A,0,sizeof(A));    memset(B,0,sizeof(B));    for(int i=n;i>=0;i--){        A[i]=p0;B[i]=1;        for(int j=1;j<=k1+k2+k3;j++){            A[i]+=A[i+j]*p[j];            B[i]+=B[i+j]*p[j];        }    }    printf("%.16lf\n",B[0]/(1-A[0]));   }    return 0;}

D - Aeroplane chess

飞行棋,还有连接飞行线可以直接到另一个点,不过状态转移还是很简单的。

#include<cstdio>#include<string.h>#include<cmath>using namespace std;typedef long long ll;const int maxn=100005;const int maxm=20005;int n,m;double dp[maxn];bool vis[maxn];bool line[maxn];int to[maxn];double DP(int i){    if(i>=n)return 0;    if(vis[i])return dp[i];    if(line[i]){vis[i]=1;return dp[i]=DP(to[i]);}    double ans=0;    for(int j=1;j<=6;j++){        ans+=1/6.0*(1+DP(i+j));    }    vis[i]=1;    return dp[i]=ans;}int main(){    int x,y;    while(~scanf("%d%d",&n,&m),n+m){        memset(line,0,sizeof(line));        for(int i=0;i<m;i++){            scanf("%d%d",&x,&y);            line[x]=1;to[x]=y;        }        memset(vis,0,sizeof(vis));        memset(dp,0,sizeof(dp));        printf("%.4lf\n",DP(0));    }    return 0;}

G - LOOPS

逃离迷宫的期望题,每一个grid都有一定概率传送到右边,上边或者自身grid,问从左下角到右上角逃脱的期望。

#include<cstdio>#include<string.h>#include<cmath>using namespace std;typedef long long ll;const int maxn=1005;const int maxm=20005;int r,c;double p[maxn][maxn][3];double dp[maxn][maxn];bool vis[maxn][maxn];double DP(int i,int j){    if(i<0||j<0)return 0;    if(vis[i][j])return dp[i][j];    double ans=0;    if(p[i][j][1])    ans+=p[i][j][1]*(2+DP(i,j+1));    if(p[i][j][2])    ans+=p[i][j][2]*(2+DP(i+1,j));    if(p[i][j][0]){    ans+=2*p[i][j][0];    ans/=(1-p[i][j][0]);    }    vis[i][j]=1;    return dp[i][j]=ans;}int main(){    while(~scanf("%d%d",&r,&c)){        for(int i=0;i<r;i++){            for(int j=0;j<c;j++){                for(int k=0;k<3;k++)                scanf("%lf",&p[i][j][k]);            }        }        memset(vis,0,sizeof(vis));        memset(dp,0,sizeof(dp));        dp[r-1][c-1]=0;        vis[r-1][c-1]=1;        for(int i=0;i<r;i++){            for(int j=0;j<c;j++){                DP(i,j);            }        }        printf("%.3lf\n",dp[0][0]);    }    return 0;}

H - Check the difficulty of problems

给出每个队伍ac每一题的概率,求冠军队伍(可以不止一支)出n题以上,其他队伍都出1题以上的概率。
求出每个队伍i出j题的概率,然后前缀和得到每个队伍i做出0到j题的概率,然后就可以得到所有队伍都a一道题以上的概率了,然后又可以求出所有队伍都出n题以下的概率,减一下就得到答案了。

#include <string.h>#include<cstdio>using namespace std;const int maxn=1000;const int maxm=35;int n,t,m;double p[maxn][maxm];double dp[maxn][maxm][maxm];double s[maxn][maxm];int main(){   while(scanf("%d%d%d",&m,&t,&n),m+t+n){    memset(dp,0,sizeof(dp));    memset(s,0,sizeof(s));    for(int i=0;i<t;i++){        for(int j=1;j<=m;j++){            scanf("%lf",&p[i][j]);        }        dp[i][0][0]=1;    }    for(int i=0;i<t;i++){        for(int j=1;j<=m;j++){            for(int k=0;k<=j;k++){                if(j)                dp[i][j][k]=dp[i][j-1][k-1]*p[i][j]+dp[i][j-1][k]*(1-p[i][j]);                else dp[i][j][k]=dp[i][j-1][k]*(1-p[i][j]);            }        }    }    for(int i=0;i<t;i++){        s[i][0]=dp[i][m][0];        for(int j=1;j<=n;j++){            s[i][j]=s[i][j-1]+dp[i][m][j];        }    }    double pp=1;    for(int i=0;i<t;i++){        pp*=1-s[i][0];    }    double pp2=1;    for(int i=0;i<t;i++){        pp2*=s[i][n-1]-s[i][0];    }    printf("%.3lf\n",pp-pp2);   }    return 0;}

I - Bag of mice

两个人轮流抓老鼠,知道怎么转移状态就行了。

#include<iostream>#include<algorithm>#include<string.h>#include<string>#include<cmath>#include<stdio.h>using namespace std;int w,b;double v[1005][1005];double dfs(int white,int black){    if(white==0)return 0.0;//only black    else if(black==0){return 1.0;}//only white    if(v[white][black]!=0)return v[white][black];    double win=0;    double cw=double(white)/double(white+black);    win+=double(white)/double(white+black);//gongzhuchoubai    cw=(1.0-cw)*double(black-1)/double(white+black-1);//mowang chou hei    double cnt=0.0;    if(black>=3){        cnt+=dfs(white,black-3)*(double(black-2)/double(black+white-2));    }    if(white>=1&&black>=2){        cnt+=dfs(white-1,black-2)*(double(white)/double(black+white-2));    }    win+=cw*cnt;    v[white][black]=win;    return win;}int main(){    scanf("%d%d",&w,&b);    printf("%.9lf",dfs(w,b));    return 0;}

J - Football

足球比赛,求某队成为冠军的概率最大,需要注意的就是某个队伍i在第j轮可以遇到的对手是那些队伍(因为赛制原因),dp[i][j]表示队伍i在第j轮获胜的概率。

#include<cstdio>#include<string.h>#include<cmath>using namespace std;typedef long long ll;const int maxn=105;const int maxm=20005;int n,m;int total;double p[200][200];double dp[200][10];bool vis[200][200];double DP(int i,int j){    if(vis[i][j])return dp[i][j];    double ans=0;    int cnt=pow(2,j);    int last=pow(2,j-1);    int k1=i/last;    int kk=i/cnt;    for(int z=kk*cnt;z<(kk+1)*cnt;z++){        if(z>=k1*last&&z<(k1+1)*last)continue;        ans+=DP(z,j-1)*p[i][z];    }    ans*=dp[i][j-1];    vis[i][j]=1;    return dp[i][j]=ans;}int main(){    while(scanf("%d",&n),n!=-1){        memset(dp,0,sizeof(dp));        memset(vis,0,sizeof(vis));        total=pow(2,n);        for(int i=0;i<total;i++){            dp[i][0]=1;            vis[i][0]=1;            for(int j=0;j<total;j++){                scanf("%lf",&p[i][j]);            }        }        for(int j=1;j<=n;j++){            for(int i=0;i<total;i++){                DP(i,j);            }        }        int winner=-1;        double g=0;        for(int i=0;i<total;i++){            if(dp[i][n]>g){                g=dp[i][n];                winner=i+1;            }        }        printf("%d\n",winner);    }    return 0;}

K - Kids and Prizes

从礼物的角度来看,只有拿或者被拿两种状态,所有孩子选房间的概率都是相等不变的,所以每一个礼物被拿走的概率都相等,其实就变成了n重伯努利实验了,然后期望就是np

M - Help Me Escape

简单期望题。

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int maxn=105;const int maxm=20005;int n,l;double p;int t[maxn];int c[maxn];double dp[maxm];double cal(int cnt){    if(dp[cnt])return dp[cnt];    double ans=0;    for(int i=0;i<n;i++){        if(cnt>c[i]){            ans+=t[i]*p;        }        else{            ans+=(1+cal(cnt+c[i]))*p;        }    }    return dp[cnt]=ans;}int main(){    while(~scanf("%d%d",&n,&l)){        for(int i=0;i<n;i++){            scanf("%d",&c[i]);            t[i]=(sqrt(double(5))+1)/2.0*c[i]*c[i];        }        p=1.0/n;        memset(dp,0,sizeof(dp));        printf("%.3lf\n",cal(l));    }    return 0;}
原创粉丝点击