zoukankan      html  css  js  c++  java
  • 概率dp作业

    概率dp特征: 概率DP一般求的是实际结果,在DP过程中,当前状态是由所有子状态的概率共同转移而来,所以概率DP只是利用了DP的动态而没有规划(即只需转移无需决策)。-------qkoqhh

    A - Collecting Bugs

    题意:一个软件有s个子系统,会产生n种bug。某人一天发现一个bug,这个bug属于某种bug,发生在某个子系统中。

    求找到所有的n种bug,且每个子系统都找到bug,这样所要的天数的期望。
    需要注意的是:bug的数量是无穷大的,所以发现一个bug,出现在某个子系统的概率是1/s,属于某种类型的概率是1/n。

    这题放在第一个,自然是入门题目,这里我在仔细温习下聚聚讲的状态和转移:

    1 f[i][j]:表示已经找到i种bug,分j类,找完剩下bug需要天数的期望。
    2 转移分4中情况,即分类和系统对于已知和未知的4种组合。
    3 相应概率也可以得出。注意4种状态对应的概率求和为1
    4 边界:f[n][s]=0 求f[0][0]
    5 方程:dp[i][j]=(n-i)/n*(s-j)/s*dp[i+1][j+1]+(n-i)/n*j/s*dp[i+1][j]+i/n*(s-j)/s*dp[i][j+1]+i/n*j/s*dp[i][j]+1

    这里方程后面加1因为这是计算下一状态的期望,所以+1。

     1 #include <cstdio>
     2 #include <iostream>
     3 using namespace std;
     4 const int maxn = 1010;
     5 double dp[maxn][maxn];
     6 int main() 
     7 {
     8     //freopen("in.txt", "r", stdin);
     9     int n, s;
    10     while(scanf("%d%d", &n, &s) != EOF){
    11         dp[n][s] = 0;
    12        for(int i=n; i>=0; i--){
    13             for(int j=s; j>=0; j--){
    14                 if(i==n && j==s)continue;
    15                 dp[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)+n*s)/(n*s-i*j);
    16             }
    17         }
    18         printf("%.4f
    ", dp[0][0]);
    19     }
    20     return 0;
    21 }
    View Code

    B - Card Collector

     题意:

    集齐n张不同概率(p1,p2……pn)的卡片(一包最多一张)需要买多少包小吃,求期望。

    Thinkging:

    看数据范围n<=20,可以思考状态压缩(qkoqhh说的)。

    这题状态也比较好定义 dp[s]: 已经收集到一些卡牌,状态压缩为s,要收集到所有卡牌的期望。 

    这题方程也比较好写

    dp[s] =   Σ dp[s]*(q1+q2)           (s>>j & 1)==1  //q1:没有卡片的概率  q2:抽出集合中存在的卡片 
           +  Σ dp[s | 1<<j] * p[j]     (s>>j & 1)==0
     1 #include <cstdio>
     2 #include <cstring>
     3 const int maxn = 21;
     4 double P[maxn];
     5 double dp[1<<maxn];
     6 int main(){
     7 //    freopen("in.txt", "r", stdin);
     8     int n;
     9     while(scanf("%d", &n) != EOF){
    10         double p = 0;
    11         for(int i=0; i<n; i++){
    12             scanf("%lf", &P[i]);
    13             p += P[i];
    14         }
    15         p = 1 - p;   //袋里没有卡片的概率 
    16         dp[(1<<n)-1] = 0;
    17         for(int s=(1<<n)-2; s>=0; s--){
    18             double x = 0, y = 1;
    19             for(int j=0; j<n; j++){
    20                 if((s>>j) & 1){
    21                     x += P[j];
    22                 }else{
    23                     y += P[j]*dp[s | (1<<j)];
    24                 }
    25                 dp[s] = y/(1-x-p);
    26             }
    27         }
    28         printf("%.5f
    ", dp[0]);
    29     }
    30     return 0;
    31 }
    View Code

    C - Aeroplane chess

    题意:在一个从0到n的格子上掷色子,从0点出发,掷了多少前进几步,同时有些格点直接相连,即若a,b相连,当落到a点时直接飞向b点。求走到n或超出n期望掷色子次数。

    Thinking:

    如果只是摇色子不飞行的话有如下转移:

    dp[i]:从i走到n的期望步数
    dp[i]= Σ 1/6*dp[i+k]+1    k=1..6

    加入飞行方式后两点的期望步数相等

    这里可以用并查集维护飞行点的dp转移,注意并查集根部维护的是目标方向的结点

    也可以用链式存储转移的关系,同样反向建立链接关系。

     1 #include <cstdio>
     2 #include <cstring>
     3 using namespace std;
     4 const int maxn = 100010;
     5 double dp[maxn];
     6 int f[maxn];
     7 int find(int x){
     8     return x==f[x]?x:f[x]=find(f[x]);
     9 }
    10 int main(){
    11 //    freopen("in.txt", "r", stdin);
    12     int n, m;
    13     while( scanf("%d%d", &n, &m) != EOF && n){
    14         memset(dp, 0, sizeof(dp));
    15         memset(f, 0, sizeof(f));
    16         for(int i=1; i<=n+6; i++){
    17             f[i] = i;
    18         }
    19         for(int i=0; i<m; i++){
    20             int x, y;
    21             scanf("%d%d", &x,&y);
    22             f[find(x)] = find(y);
    23         }
    24         for(int i=n-1; i>=0; i--){
    25             if(find(i) == i){  //这是不能跳跃的点 
    26                 for(int j=i+1; j<=i+6; j++){
    27                     dp[i] += (dp[find(j)] + 1)/6;
    28                 }
    29             }
    30         }
    31         printf("%.4f
    ", dp[0]); 
    32     }
    33 }
    View Code
     1 #include <cstdio>
     2 #include <cstring>
     3 #include <vector>
     4 using namespace std;
     5 const int maxn = 100010;
     6 double dp[maxn];
     7 bool vis[maxn];
     8 vector<int> G[maxn];
     9 int main(){
    10 //    freopen("in.txt", "r", stdin);
    11     int n, m;
    12     while( scanf("%d%d", &n, &m) != EOF && (n+m)){
    13         memset(dp, 0, sizeof(dp));
    14         memset(vis, 0,  sizeof(vis)); 
    15         for(int i=0; i<=n; i++)G[i].clear();
    16         for(int i=0; i<m; i++){
    17             int x, y;
    18             scanf("%d%d", &x,&y);
    19             G[y].push_back(x); 
    20             /*
    21              *反向建立链表,存储直接跳转的点对,方便下面逆着转移 
    22             */ 
    23         }
    24         for(int i=0; i<G[n].size(); i++){
    25             vis[G[n][i]] = true;
    26         }//与终点相连的点可直接跳转 
    27         for(int i=n-1; i>=0; i--){
    28             if(!vis[i]){  //这是不能跳跃的点 
    29                 for(int j=i+1; j<=i+6; j++){
    30                     dp[i] += dp[j]/6;
    31                 }
    32                 dp[i] += 1;
    33                 vis[i] = true; 
    34             }
    35             for(int j=0; j<G[i].size(); j++){
    36                 dp[G[i][j]] = dp[i];
    37                 vis[G[i][j]] = true;
    38             }//可以直接跳转的点 
    39         }
    40         printf("%.4f
    ", dp[0]); 
    41     }
    42     return 0;
    43 }
    View Code

    E - Bag of mice

    题意:一个袋子里有n个白老鼠,m个黑老鼠,王子和龙依次取,王子先取,先取到白老鼠的为胜者,其中龙取老鼠的时候,取出一只后,会有随机的一只老鼠跑出来,而且取老鼠的时候,每只老鼠取到的概率是一样的,跑出来的概率也是一样的,算王子赢的概率。

    这里学习了kuangbin对这题的解析。看tutorial里用将问题分解为两个子问题(W,B)->(W,B-1)+(W-1,B-1)+(W,B-2),这里可以设置一个标记回合的参数。

     1 #include <cstdio>
     2 #include <iostream>
     3 #include <cstdlib>
     4 #include <cmath>
     5 #include <cstring>
     6 #include <algorithm>
     7 using namespace std;
     8 #define LL long long
     9 
    10 const int maxn = 1010;
    11 double dp[maxn][maxn];
    12 //dp[i][j]:到王妃抓时还剩 i只白鼠j只黑鼠时王妃胜的概率 
    13 int main(){
    14     //freopen("in.txt", "r", stdin);
    15     memset(dp, 0, sizeof(0));
    16     int w,b; 
    17     scanf("%d%d", &w, &b);
    18     for(int i=0; i<=b; i++)dp[0][i]=0;
    19     // 没有白鼠时胜率为0 
    20     for(int i=1; i<=w; i++)dp[i][0]=1;
    21     //没有黑鼠时 胜率为1
    22     for(int i=1; i<=w; i++){
    23         for(int j=1; j<=b; j++){
    24             dp[i][j] += (double)i/(i+j);//王妃抓到一只白鼠
    25             if(j>=3){
    26                 dp[i][j] += ((double)j/(i+j)) * ((double)(j-1)/(i+j-1)) * ((double)(j-2)/(i+j-2)) * dp[i][j-3];
    27             }//王妃抓到黑,龙抓到一个黑,放了一个黑 
    28             if(j>=2){
    29                 dp[i][j] += ((double)j/(i+j)) * ((double)(j-1)/(i+j-1)) * ((double)i/(i+j-2)) * dp[i-1][j-2];
    30             }//王妃抓到黑,龙抓到黑,放了一个白 
    31         }
    32     } 
    33     printf("%.9lf
    ", dp[w][b]);
    34     return 0;
    35 }
    View Code
     1 double dfs(int w, int b, int flag){
     2     if(w == 0) return flag;  //flag=1:此轮龙抓,龙胜返回1; 代表两人都没抓到白鼠,龙胜 
     3     if(b == 0) return 1;
     4     double &memo = dp[w][b][flag];
     5     if(memo > -0.5) return memo;
     6     double ans = 0;
     7     if(flag == 0){
     8         ans += (double)w/(w + b);   //王妃抓白鼠胜 
     9         ans += ((double)b/(w + b)) * (1 - dfs(w, b-1, 1));
    10         //王妃抓黑鼠,下一轮龙抓失败的概率(dfs()返回成功的概率) 即王妃胜 
    11     }else{
    12         ans += (double)w/(w + b);   //龙抓白鼠胜的概率
    13         ans += ((double)b/(w + b)) * ((double)w/(w+b-1)) * (1 - dfs(w-1, b-1, 0));
    14         // 龙抓一个黑鼠放一个白鼠胜的概率 
    15         if(b > 1) { //龙抓一个黑鼠 放一个黑鼠胜的概率 
    16             ans += ((double)b/(w + b)) * ((double)(b-1)/(w + b-1)) * (1 - dfs(w, b-2, 0));
    17         }
    18     }
    19     return memo = ans;
    20 }
    21 dp[i][j][0]=dp[i][j][1] = -1
  • 相关阅读:
    在包a中新建一个类A,在类A中有一个int add(int m)方法,用来求1+2+…+m 的和。在包b中新建一个类B,在类B中有一个int cheng(int n)方法,用来求n! 的结果。在包c中新建一个主类C,调用A、B中的方法输出1+2+…+30的和, 以及5!的计算结果。
    在包a中编写一个类Father,具有属性:年龄(私有)、姓名(公有); 具有功能:工作(公有)、开车(公有)。 在包a中编写一个子类Son,具有属性:年龄(受保护的)、姓名; 具有功能:玩(私有)、学习(公有)。 最后在包b中编写主类Test,在主类的main方法中测试类Father与类Son。
    简单且线程安全的两个单例模式java程序
    Condition的优点
    Java设计模式—生产者消费者模式(阻塞队列实现)
    Java NIO使用及原理分析 (一)
    一步步优化JVM五:优化延迟或者响应时间(1)
    UML 类图
    Eclipse中构建Fluent风格到Formatter
    Memcache 问题集锦
  • 原文地址:https://www.cnblogs.com/seaupnice/p/9482175.html
Copyright © 2011-2022 走看看