• 概率dp小结


    好久之前学过,记得是一次亚洲区的前几天看了看概率dp,然后亚洲区就出了一道概率dp,当时虽然做上了,但是感觉有很多地方没懂,今天起早温习了一下,觉得很多地方茅塞顿开,果然学习的话早上效果最好了。

    首先来看一道最基础概率dp

    题意是,有一个软件,有s个子系统,会产生n种bug。 
    某个程序员一天能发现一个bug,这个bug是这n种bug中的一种,然后发生在某个子系统中。 
    问,找到所有的n种bug,且每个子系统都找到bug,这样所要的天数,的期望。

    期望,可以分解成多个子期望的加权和,权为子期望发生的概率 
    所以: 我首先想到了一个这样的公式dp[x][y] = dp[x][y]*p1+dp[x-1][y-1]*p2+dp[x][y-1]*p3+dp[x-1][y]*p4

    dp[x][y]代表已经有x种bug并且有y个系统至少有一个bug的期望值

    我们知道他是从自身以及他的前几种状态推导过来,乍一看这个公式应该是对的,但是dp[n][m]会无穷大,因为他的期望是没有停止状态的,也就是题意要求的应该是到达n,m状态时停止。

    那么我们又可以从倒推的角度去考虑这个问题,dp[x][y]表示的是已经有x种bug并且有y个系统至少有一种bug的时候还需要多少步能够到达dp[n][m]的状态。

    那么公式又变成了这样dp[x][y] = dp[x+1][y+1]*p1+dp[x+1][y]*p2+dp[x][y+1]*p3+dp[x][y]*p4+1

    也就是dp[x][y] = dp[x+1][y+1] *(n-x)*(m-y)/n/m+dp[x+1][y]*(n-x)*y/n/m+dp[x][y+1]*x*(m-y)/n/m+dp[x][y]*x*y/n/m+1

    将dp[x][y]合并得dp[x][y](1-x*y/n/m) = dp[x+1][y+1] *(n-x)*(m-y)/n/m+dp[x+1][y]*(n-x)*y/n/m+dp[x][y+1]*x*(m-y)/n/m+1

    1.poj2096,就是上面的题

    #include<stdio.h>
    #include<string.h>
    #include<iostream>
    using namespace std;
    double dp[1004][1004];
    int main(){
        int n, m;
        scanf("%d%d", &n, &m);
            for(int i =0 ;i <= n+1; i++){
                for(int k = 0; k <= m+1; k++){
                    dp[i][k]= 0;
                }
            }
            for(int x = n; x >= 0; x--){
                for(int y = m; y >=0; y--){
                    if(x == n && y ==m)dp[x][y] = 0;
                    else
                    dp[x][y]= (1+dp[x+1][y+1] *(n-x)*(m-y)/n/m+dp[x+1][y]*(n-x)*y/n/m+dp[x][y+1]*x*(m-y)/n/m)/(1.0-1.0*x*y/n/m);
                }
            }
            printf("%.4f
    ", dp[0][0]);
    }
    View Code

     2.UVALive 5811 

    题目大意:有54张牌,一张一张翻,在四种花色都不小于分别给定的四个值时停止,王牌可以当做任意一种花色,但是在翻得时候就要确定,求翻牌数的期望。

    公式神马的好推,写的时候稍微费劲了点

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    double dp[15][15][15][15][5][5];
    int main(){
        int t;
        scanf("%d", &t);
        int cas = 1;
        while(t--){
            int a, b, c, d;
            scanf("%d%d%d%d", &a, &b, &c, &d);
            int aa = 0;
            if(a > 13) aa += 13-a;
            if(b > 13) aa += 13-b;
            if(c > 13) aa += 13 - c;
            if(d > 13) aa += 13 - d;
            if(aa < -2){
                printf("Case %d: %.3f
    ", cas++, -1.0);//printf("000");
                continue;
            }
            for(int i = 13; i >= 0; i--){
                for(int k = 13; k >= 0;k --){
                    for(int j = 13; j >= 0; j--){
                        for(int h = 13; h >= 0; h--){
                            for(int x = 4; x >= 0; x--){
                                for(int y = 4; y >= 0; y--){
                                    int aa = i, bb = k, cc = j, dd = h;
                                    if(x == 1) aa++;
                                    if(x == 2) bb ++;
                                    if(x == 3) cc++;
                                    if(x == 4) dd++;
                                    if(y == 1) aa++;
                                    if(y==2) bb++;
                                    if(y == 3) cc++;
                                    if(y == 4) dd++;
                                    int tot = 54 - aa - bb - cc- dd;
                                    if(aa >= a && bb >= b && cc >= c && dd >= d){
                                        dp[i][k][j][h][x][y] = 0;continue;
                                    }else if(tot == 0){
                                        dp[i][k][j][h][x][y] = 10000000000;continue;
                                    }
                                    double uu = 0;
                                    int ss = 0;                     //kexuanwangpai
                                    if(!x) ss ++;
                                    if(!y) ss ++;
                                    dp[i][k][j][h][x][y] = 1;
                                    if(!x){
                                        uu = min(min(min(dp[i][k][j][h][1][y], dp[i][k][j][h][2][y]), dp[i][k][j][h][3][y]), dp[i][k][j][h][4][y]);
                                        //if(i+k+j+h == 52 && x == 0 && y == 0) printf("%.3lf %.3lf-----------
    ", uu,ss*1.0/tot*uu);
                                        dp[i][k][j][h][x][y] += ss*1.0/tot*uu;
                                    }
                                    else if(!y){
                                        uu = min(min(min(dp[i][k][j][h][x][1],dp[i][k][j][h][x][2]),dp[i][k][j][h][x][3]), dp[i][k][j][h][x][4]);
                                        dp[i][k][j][h][x][y] += ss * 1.0/tot*uu;
                                    }
                                    uu = 0;
                                    if(i < 13){
                                        uu += dp[i+1][k][j][h][x][y]*(13-i)/tot;
                                    }
                                    if(k < 13)
                                        uu +=dp[i][k+1][j][h][x][y] * (13-k)/tot;
                                    if(j < 13)
                                        uu += dp[i][k][j+1][h][x][y] *(13 - j)/tot;
                                    if(h < 13)
                                        uu += dp[i][k][j][h+1][x][y] * (13 - h)/tot;
                                    dp[i][k][j][h][x][y] += uu;
                                }
                            }
                        }
                    }
                }
            }
            //printf("%.3lf
    ", dp[13][13][13][13][0][0]);
            printf("Case %d: %.3f
    ", cas++, dp[0][0][0][0][0][0]);
        }
    }
    View Code
  • 相关阅读:
    sc delete 服务器名提示“指定的服务已经标记为删除”
    visio2007无法拖动
    Oracle 时间相减得出毫秒、秒、分、时、天,,【转】
    服务启动错误1053解决方案之一
    【转】oracle 删除重复记录
    利用VS2013 XSLT对 XML进行转换
    xpath属性值的模糊匹配
    android 与 小米1S刷机学习
    如何添加打印机
    SVN学习(一)——SVN 检出文件步骤、图标显示及含义
  • 原文地址:https://www.cnblogs.com/icodefive/p/4712477.html
Copyright © 2020-2023  润新知