zoukankan      html  css  js  c++  java
  • 运用记忆化搜解决的概率期望问题

    这个算是脱了很久很久的一片解题报告了。主要是运用动态规划求解期望问题的解法,一般用记忆化搜的写法更好理解。以几个题为例:

    UVALive 5811,Card。
    题意很简单:一副扑克牌共有13张黑桃,红心,梅花,方片和大小王各一张,input会给出黑桃,红心,梅花,方片的数量。问从随机分布的一堆牌中依次抽取一张牌,达到题中所要求的数量,至少需要抽取牌的期望,如果抽到大小王,则可以把它们当成任意花色,因为要求抽取的牌数最少,所以当然是当成最需要的了。
    比赛训练时想了很久,都没有想出来,最后还是看了大牛的代码才略懂一二。主要是用dp值表示期望,记忆化搜直接出解,dp[a][b][c][d][big][small]表示抽取了a张黑桃,b张红心,c张梅花,d张方片,大小王分别当成了big和small(为4表示还没有用过大小王,0表示黑桃,1表示红心,2表示梅花,3表示方片),其他的看代码就知道了
    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<cstring>
     6 #define see(x) cout<<#x<<":"<<x<<endl;
     7 using namespace std;
     8 const int maxn = 16;
     9 double dp[maxn][maxn][maxn][maxn][5][5];
    10 bool vis[maxn][maxn][maxn][maxn][5][5];
    11 double aa, bb, cc, dd;
    12 double dfs(int a, int b, int c, int d, int big, int small){
    13     if(vis[a][b][c][d][big][small]) return dp[a][b][c][d][big][small];
    14     vis[a][b][c][d][big][small] = 1;
    15     int now[] = {a,b,c,d};
    16     if(big<4) now[big]++;
    17     if(small<4) now[small]++;
    18     double sum = now[0]+now[1]+now[2]+now[3];
    19     if(now[0]>=aa&&now[1]>=bb&&now[2]>=cc&&now[3]>=dd) return dp[a][b][c][d][big][small] = sum;
    20     double tmp = 0.0;
    21     if(a<13) tmp += dfs(a+1,b,c,d,big,small)*(13.0-a)/(54.0-sum);
    22     if(b<13) tmp += dfs(a,b+1,c,d,big,small)*(13.0-b)/(54.0-sum);
    23     if(c<13) tmp += dfs(a,b,c+1,d,big,small)*(13.0-c)/(54.0-sum);
    24     if(d<13) tmp += dfs(a,b,c,d+1,big,small)*(13.0-d)/(54.0-sum);
    25     if(big==4){
    26         double tmp1 = 1e10;
    27         tmp1 = min(tmp1,dfs(a,b,c,d,0,small));
    28         tmp1 = min(tmp1,dfs(a,b,c,d,1,small));
    29         tmp1 = min(tmp1,dfs(a,b,c,d,2,small));
    30         tmp1 = min(tmp1,dfs(a,b,c,d,3,small));
    31         tmp += tmp1/(54.0-sum);
    32     }
    33     if(small==4){
    34         double tmp1 = 1e10;
    35         tmp1 = min(tmp1,dfs(a,b,c,d,big,0));
    36         tmp1 = min(tmp1,dfs(a,b,c,d,big,1));
    37         tmp1 = min(tmp1,dfs(a,b,c,d,big,2));
    38         tmp1 = min(tmp1,dfs(a,b,c,d,big,3));
    39         tmp += tmp1/(54.0-sum);
    40     }
    41     if(tmp<1e-8) tmp = 1e10;  //tmp初始值是0,然后累加求解,但是如果什么都没有加过的化,期望不是0,而是无穷大。坑!!!
    42     return dp[a][b][c][d][big][small] = tmp;
    43 }
    44 
    45 int main(){
    46     int T, cas, i, j, k;
    47     double ans;
    48     scanf("%d",&T);
    49     for(cas=1;cas<=T;cas++){
    50         scanf("%lf%lf%lf%lf",&aa,&bb,&cc,&dd);
    51         if( max(0.0,aa-13)+max(0.0,bb-13)+max(0.0,cc-13)+max(0.0,dd-13)>2 ) ans = -1.0;
    52         else{
    53             memset(vis,0,sizeof(vis));
    54             ans = dfs(0,0,0,0,4,4);
    55         }
    56         printf("Case %d: %.3lf\n",cas,ans);
    57     }
    58     return 0;
    59 }

    还有一道最近zoj月赛的题,zoj3640 Help Me Escape,里面也要用到记忆化搜(lz总是手贱,忘记写成记忆化的形势,贡献一个wa)

    题意:有n个城市,每个城市有值ci,有个要从城市逃离的人,他有一个能量值f,当他到某个城市时,只要他的能量值f大于这个城市的ci值,他就可以逃离,否则他的能量值将增加ci,同时过一天后随机的被送往任一城市(也有可能还是这个城市),问这个人逃离这些城市的期望。

    比赛的时候写了一个裸的求期望,果断t了~赛后用了一下记忆化搜就过了,跟上题类似,dfs时也是从初始的状态开始递推,dp[deep][f]表示以第deep天,此人能量值为f,之后逃脱需要多少天。最后在仔细考虑一下deep和f潜在的最大值,就很好求解了~

    View Code
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cmath>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn = 105;
     8 const int inf = (1<<30);
     9 const double gold = (1+sqrt(5.0))/2;
    10 int c[maxn], n, d[maxn];
    11 double dp[maxn][10005];
    12 double ans;
    13 int cal(int c){
    14     return int(gold*c*c);
    15 }
    16 double dfs(int deep, int f){
    17     int i, j;
    18     if(dp[deep][f]>-0.5){
    19         return dp[deep][f];
    20     }
    21     double tmp = 0.0;
    22     for(i=1;i<=n;i++){
    23         if(f>c[i]){
    24             tmp += (deep+(double)d[i])/n;
    25         }
    26         else{
    27             tmp += dfs(deep+1,min(f+c[i],10001))/n;
    28         }
    29     }
    30     return dp[deep][f] = tmp;
    31 }
    32 int main(){
    33     int f, i, j, k, l;
    34     while(~scanf("%d%d",&n,&f)){
    35         for(i=1;i<=n;i++){
    36             scanf("%d",&c[i]);
    37             d[i] = cal(c[i]);
    38         }
    39         for(i=0;i<maxn;i++){
    40             for(j=0;j<10005;j++){
    41                 dp[i][j] = -1.0;
    42             }
    43         }
    44         c[0] = 0;
    45         printf("%.3lf\n",dfs(0,f));
    46     }
    47     return 0;
    48 }

     还有一些用记忆化搜不好求解的题目,也就是直接求解就好了的题~~,比如 URAL - 1776 Anniversary FireworkUVALive - 5721 Activation(这个求递推公式很奇葩,算做数学题一样~)

  • 相关阅读:
    linux内核(四)内存管理单元MMU
    open函数详解
    linux内核(三)文件系统
    C++中数字与字符串之间的转换 scanf string总结(复习必读)
    hello程序的运行过程-从计算机系统角度
    剑指offer第12题打印从1到n位数以及大整数加法乘法
    2017-10-11第二次万革始面经
    为什么需要半关闭
    Ubuntu指令
    143. Reorder List
  • 原文地址:https://www.cnblogs.com/celia01/p/2701300.html
Copyright © 2011-2022 走看看