---恢复内容开始---

题意:最开始你有x元钱,要进行M轮赌博。每一轮赢的概率为P,你可以选择赌与不赌,如果赌也可以将所持的任意一部分钱作为赌注(可以是整数,也可以是小数)。如果赢了,赌注将翻倍;输了赌注则没了。在M轮赌博结束后,如果你持有的钱在100万元以上,就可以把这些钱带回家。问:当你采取最优策略时,获得100万元以上的钱并带回家的概率是多少。

类型:动态规划&离散化思想

分析:由于每一轮的赌注是任意的,不一定为整数,因而有无限种可能,所以即便想穷竭搜索也无从着手。但如果能化连续为离散,那么可能便也是有限的了。具体如下:假设前M-1轮的赌博后,还持有x'元。对于最后一轮,考虑的情况有3种。如果x' >= 100万,则没有必要再赌了即最后一轮赢的概率为0;如果50<= x' < 100万,只要参与赌博并且赌注 >= 50万则有赢的概率为P;如果x' < 50万,那么无论是否参与最后一轮的赌博,压的赌注是多少赢的概率必为0。我们不妨看一下倒数第二轮与最后一轮的关系,设在倒数第二轮时持有的钱为x。如果x >= 100万,赢的概率为1;如果x < 25万,即便最后两轮赌博都赢了总钱数必小于100万,所以赢的概率为0;否则,只要选择参与至少一轮赌博并且赌注至少25万则有赢得概率。假设倒数第二轮的赌注为y(y = 0 或 y >= 25万),则最后一轮持有的钱x' = (x + y)或x' = (x - y)。而倒数第二轮考虑的情况具体可以分为5种。综上,当参与M轮赌博时所需考虑的情况总共有2^m + 1种,可以通过dp解决。定义一个二维dp数组,dp[i][j] := 参与第i轮赌博,持有的钱所在模块为j并且采取最优策略时赢的概率。初始化:dp[n][1 << m] = 1,状态转移方程dp[i][j] = max(P * dp[i + 1][j + k] + (1 - P) * dp[i + 1][j - k] / 0 <= k <= min(j, n - j) )。时间复杂度O(m*2^2m)。

//代码实现:已通过滚动数组循环利用完成空间复杂度的优化

#include <iostream>
#include <memory.h>
#include <algorithm>
#include <cstdio>
using namespace std;

int M , X; 
double P; 
double dp[2][(1 << 15) + 1]; 

void solve() 

   int n = 1 << M;  
     double *pre = dp[0] , *nxt = dp[1];  
   memset(pre , 0 , sizeof(double) * (n + 1));  
   pre[n] = 1.0;//因:模块n对应的资金>= 100万
     for(int r = 0 ; r < M; r++) 
     { 
     for(int i = 0 ; i <= n ; i++)   
     { 
            int step = min(i , n - i);//避免i + j > n
            double t = 0.0; 

        for(int j = 0 ; j <= step ; j++)      
        t = max(t , P * pre[i + j] + (1 - P) * pre[i - j]);//进行m轮赌博,最小赌资应为 100万/n  
       nxt[i] = t;//以模块i的资金进行r + 1轮赌博,赢的最大概率
     }  
      swap(pre , nxt);  
   }  
   int i = X * n / 1000000;//资金X所在模块
   printf("%.6lf\n" , pre[i]); 

int main()
{
   cin >> M >> P >> X;
   solve();
   return 0;
}

---恢复内容结束---