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

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

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

你可能感兴趣的:(问题)