运用记忆化搜解决的概率期望问题

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

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(这个求递推公式很奇葩,算做数学题一样~)

原文地址:https://www.cnblogs.com/celia01/p/2701300.html