• 概率dp入门篇


    概率dp入门篇

     

    1.hdu 3853 LOOPS

      思路:用dp[i][j]表示在i,j点的期望步数,p[i][j][k](k=0-2)表示i,j点的3个概率,假设所有期望都是已知:

        则dp[i][j]=p[i][j][0]*(dp[i][j]+2)+p[i][j][1]*(dp[i][j+1]+2)+p[i][j][2]*(dp[i+1][j]+2);

      由于p[i][j][0]+p[i][j][1]+p[i][j][2]=1为了方便表示,可以把2提出来写成:

        dp[i][j]=p[i][j][0]*dp[i][j]+p[i][j][1]*dp[i][j+1]+p[i][j][2]*dp[i+1][j]+2;

      现在等号左右两边都有dp[i][j],移项得:

        dp[i][j]=(p[i][j][1]*dp[i][j+1]+p[i][j][2]*dp[i+1][j]+2)/(1-p[i][j][0]);

      现在解法很明显了,要求dp[i][j],先知道dp[i+1][j]和dp[i][j+1]即可,由于dp[r-1][c-1]是已知的0,所以倒推即可得到所有的dp[i][j].

    HDU 3853 
    #include<stdio.h>
    double p[1010][1010][3];
    double dp[1010][1010];
    int main()
    {
        int r,c,i,j,k;
        while(scanf("%d%d",&r,&c)!=EOF){
            for(i=0;i<r;i++)
                for(j=0;j<c;j++)
                    for(k=0;k<3;k++)
                        scanf("%lf",&p[i][j][k]);
            dp[r-1][c-1]=0;
            p[r-1][c-1][0]=1;
            for(i=r-1;i>=0;i--){
                for(j=c-1;j>=0;j--){
                    if(p[i][j][0]==1) continue;
                    double factor=1/(1-p[i][j][0]);
                    dp[i][j]=(p[i][j][1]*dp[i][j+1]+p[i][j][2]*dp[i+1][j]+2)*factor;
                }
            }
            printf("%.3lf\n",dp[0][0]);
        }
        return 0;
    }

     2.hdu 4405 Aeroplane chess

      思路:同样是倒推,如果当前点可以传送,则该点的期望直接等于它传送到的那个点,否则可列出下式:

      dp[i]=∑(dp[i+k]+1)*p[k]     (k取1-6,p[k]=1/6)

    HDU 4405
    #include<stdio.h>
    #include<string.h>
    double dp[100020];
    int next[100020];
    int main()
    {
        int n,m,i,x,y,k;
        while(scanf("%d%d",&n,&m)!=EOF)
        {
            if(n==0&&m==0) break;
            for(i=0;i<=n+6;i++)
                dp[i]=next[i]=0;
            while(m--){
                scanf("%d%d",&x,&y);
                next[x]=y;
            }
            double temp=1.0/6;
            for(i=n-1;i>=0;i--){
                if(next[i]){
                    dp[i]=dp[next[i]];
                    continue;
                }
                for(k=1;k<=6;k++){
                    dp[i]+=(dp[i+k]+1)*temp;
                }
            }
            printf("%.4lf\n",dp[0]);
        }
        return 0;
    }

    3.zoj 3329 One Person Game

      该题的想法学习自该博客:http://blog.csdn.net/morgan_xww/article/details/6775853

      思路:上一题的升级版,每次可以扔三个骰子,且数字个数不定,可列出式子:

      dp[i]=dp[0]*p0+∑dp[i+k]*P[k]  (P0即回到0点的概率,P[k]是点数总和是k的概率)

      而我们想求的也是dp[0],明显当前的递推式是有环的,因为P[0]在每一项都出现,我们假设P[0]可以表示任一项,即设:

      dp[i]=fa[i]*dp[0]+fb[i];

      带入1式可得:

      dp[i]=(∑P[k]*fa[i+k]+p0)*dp[0]+∑P[k]*fb[i+k]+1;

      和1式的每一项对应可得:

      fa[i]=∑P[K]*fa[i+k]+p0;

      fb[i]=∑P[K]*fb[i+k]+1;

      倒推出所有的a和b,最后答案就是dp[0]=fb[0]/(1-fa[0])

      

    ZOJ 3329
    #include<stdio.h>
    #include<string.h>
    double fa[550],fb[550];
    double p[40];
    int main(){
        int T,i,j,k,n,k1,k2,k3,a,b,c;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d%d%d%d%d",&n,&k1,&k2,&k3,&a,&b,&c);
            memset(fa,0,sizeof(fa));
            memset(fb,0,sizeof(fb));
            memset(p,0,sizeof(p));
            int max=k1+k2+k3;
            double temp=1.0/(k1*k2*k3);
            for(i=1;i<=k1;i++)
                for(j=1;j<=k2;j++)
                    for(k=1;k<=k3;k++)
                        if(i!=a||j!=b||k!=c)
                            p[i+j+k]+=temp;
            for(i=n;i>=0;i--){
                for(j=3;j<=max;j++){
                    fa[i]+=fa[i+j]*p[j];
                    fb[i]+=fb[i+j]*p[j];
                }
                fa[i]+=temp;
                fb[i]+=1;
            }
            printf("%.15lf\n",fb[0]/(1-fa[0]));
        }
        return 0;
    }

    4.poj 2096 Collecting Bugs

      思路:用dp[i][j]表示当前已经找到了i个子系统j个bug,可得到:

        dp[i][j]=((dp[i][j]*i*j+dp[i+1][j]*(n-i)*j+dp[i][j+1]*i*(s-j)+dp[i+1][j+1])*(n-i)*(s-j)+1 )/n*s;

      化简同第一题.

    POJ 2096 
    #include<stdio.h>
    #include<string.h>
    double dp[1010][1010];
    int main()
    {
        int n,s,i,j;
        while(scanf("%d%d",&n,&s)!=EOF){
            memset(dp,0,sizeof(dp));
            for(i=n;i>=0;i--){
                for(j=s;j>=0;j--){
                    if(i==n&&j==s) continue;
                    double factor=n*s-i*j;
                    dp[i][j]=dp[i+1][j]*j*(n-i)+dp[i][j+1]*(s-j)*i+dp[i+1][j+1]*(n-i)*(s -j)+n*s;
                    dp[i][j]/=factor;
                }
            }
            printf("%.4lf\n",dp[0][0]);
        }
        return 0;
    }

    5.hdu 4050 wolf5x

      思路:这题可以有两种思考的方向,即正推和反推,不过明显反推的方法实现简单点而且效率要高.

      反推法:

    HDU 4050
    #include<stdio.h>
    #include<string.h>
    double p[4100][4],dp[4100][4];
    int main()
    {
        int T,i,j,k,n,a,b;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&n,&a,&b);
            memset(p,0,sizeof(p));
            memset(dp,0,sizeof(dp));
            for(i=1;i<=n;i++)
                for(j=0;j<4;j++)
                    scanf("%lf",&p[i][j]);
            for(i=n+1;i<=4000;i++)
                p[i][3]=1;
            p[0][3]=dp[0][3]=1;
            for(i=0;i<=n;i++)
            {
                for(j=1;j<4;j++)
                {
                    double NOT=1;
                    for(k=a;k<=b;k++)
                    {
                        if(j==1){
                            dp[i+k][2]+=dp[i][j]*p[i+k][2]*NOT;
                            dp[i+k][3]+=dp[i][j]*p[i+k][3]*NOT;
                            NOT*=(p[i+k][0]+p[i+k][1]);
                        }
                        if(j==2){
                            dp[i+k][1]+=dp[i][j]*p[i+k][1]*NOT;
                            dp[i+k][3]+=dp[i][j]*p[i+k][3]*NOT;
                            NOT*=(p[i+k][0]+p[i+k][2]);
                        }
                        if(j==3){
                            dp[i+k][1]+=dp[i][j]*p[i+k][1]*NOT;
                            dp[i+k][2]+=dp[i][j]*p[i+k][2]*NOT;
                            dp[i+k][3]+=dp[i][j]*p[i+k][3]*NOT;
                            NOT*=(p[i+k][0]);
                        }
                        if(k+i>n) break;
                    }
                }
            }
            double ans=0;
            for(i=1;i<=n+a;i++)
                for(j=0;j<4;j++)
                    ans+=dp[i][j];
            printf("%.8lf\n",ans);
        }
        return 0;
    }

      正推法:

    HDU 4050
    #include<stdio.h>
    #include<string.h>
    double p[2010][4],dp[2010][4];
    int main(){
        int T,n,a,b,i,j,k;
        scanf("%d",&T);
        while(T--){
            scanf("%d%d%d",&n,&a,&b);
            for(i=1;i<=n;i++)
                scanf("%lf%lf%lf%lf",&p[i][0],&p[i][1],&p[i][2],&p[i][3]);
            memset(dp,0,sizeof(dp));
            p[0][3]=1;
            p[0][1]=p[0][2]=p[0][0]=0;
            for(i=n;i>=0;i--){
                for(j=1;j<=4;j++){
                    double NOT=1,temp;
                    for(k=a;k<=b;k++){
                        if(k+i>n){
                            dp[i][j]+=NOT;
                            break;
                        }
                        if(j==1){
                            temp=(dp[i+k][2]+1)*p[i+k][2]+(dp[i+k][3]+1)*p[i+k][3];
                            dp[i][j]+=temp*NOT;
                            NOT*=(p[i+k][0]+p[i+k][1]);
                        }
                        if(j==2){
                            temp=(dp[i+k][1]+1)*p[i+k][1]+(dp[i+k][3]+1)*p[i+k][3];
                            dp[i][j]+=temp*NOT;
                            NOT*=(p[i+k][0]+p[i+k][2]);
                        }
                        if(j==3){
                            temp=(dp[i+k][1]+1)*p[i+k][1]+(dp[i+k][2]+1)*p[i+k][2]+(dp[i+k][3]+1)*p[i+k][3];
                            dp[i][j]+=temp*NOT;
                            NOT*=(p[i+k][0]);
                        }
                    }
                }
            }
            printf("%.8lf\n",dp[0][3]);
        }
        return 0;
    }


    6.hdu 4336 Card Collector

      思路:状态+递推,容易列出式子:dp[i]=dp[i]*P(空袋子+已有卡片)+dp[j]*P[j]+1;

      j表示i状态中不包含的卡片,dp[j]表示i增加第j张卡片后的状态,P[j]表示第j张卡片出现的概率,移项后递推就行了.

      注意:样例有点坑..如果只保留3位无法满足精度要求.

    HDU 4336
    #include<stdio.h>
    double p[25],dp[2000000];
    
    int main(){
        int n,i,j;
        while(scanf("%d",&n)!=EOF){
            double empty=1;
            for(i=0;i<n;i++){
                scanf("%lf",&p[i]);
                empty-=p[i];
            }
            int maxstate=(1<<n)-1;
            dp[maxstate]=0;
            for(i=maxstate-1;i>=0;i--){
                double tp=empty,temp=0;
                for(j=0;j<n;j++){
                    if(i&(1<<j))
                        tp+=p[j];
                    else
                        temp+=(dp[i+(1<<j)])*p[j];
                }
                dp[i]=(temp+1)/(1-tp);
            }
            printf("%.8lf\n",dp[0]);
        }
        return 0;
    }

     

  • 相关阅读:
    扩展方法的定义及使用
    HTTP协议及POST与GET操作差异,C#中如何使用POST、GET等
    C#多线程学习(三) 生产者和消费者
    .NET设计模式(18):迭代器模式(Iterator Pattern)
    Net设计模式实例之备忘录模式(Memento Pattern)
    安装中文VS2008 SP1 和.NETFRAMEWORK 3.5SP1后智能提示是英文的解决办法
    ASP.NET MVC 入门系列教程
    在ASP.NET MVC中使用DropDownList
    .NET设计模式(2):单件模式(Singleton Pattern)
    想要成功,请记住!
  • 原文地址:https://www.cnblogs.com/SolarWings/p/3040784.html
Copyright © 2020-2023  润新知