题意:最开始你有x元钱,要进行M轮赌博。每一轮赢的概率为P,你可以选择赌与不赌,如果赌也可以将所持的任意一部分钱作为赌注(可以是整数,也可以是小数)。如果赢了,赌注将翻倍;输了赌注则没了。在M轮赌博结束后,如果你持有的钱在100万元以上,就可以把这些钱带回家。问:当你采取最优策略时,获得100万元以上的钱并带回家的概率是多少。
samples:
input:
M = 1, P = 0.5, X = 500000
output:
0.500000
input:
M = 3, P = 0.75, X = 600000
output:
0.843750
类型:动态规划&离散化思想
连续性是本问题的一大特点。每一轮可押的金钱不一定是整数,因而有无限种可能,所以无法穷竭搜索。
分析如下:
假设前M-1轮的赌博后,还持有x‘
元。对于最后一轮,考虑的情况有3种。如果x‘
>= 100万,则没有必要赌下去的必要;如果50<= x‘
< 100万,只要参与赌博并且赌注 >= 50万则有赢的概率为P;如果x‘
< 50万,那么无论是否参与最后一轮的赌博,压的赌注是多少赢的概率必为0。
现在考虑一下最后两轮的情况,最后二轮考虑的情况具体可以分为5种。设在倒数第二轮时持有的钱为x‘‘
。如果x‘‘
>= 100万,赢的概率为1;如果x‘‘
< 25万,即便最后两轮赌博都赢了也无济于事,所以赢的概率为0;否则,只要选择参与至少一轮赌博并且赌注至少25万则有获胜概率,具体获胜概率与持有的资金有关。
综上,可以发现,当参与M轮赌博时所需考虑的情况总共有2^m + 1种,某个范围中,即使所持的钱数不同,最后可以带钱回家的概率也是完全一样的。可以通过dp解决。
定义一个二维dp数组,dp[i][j] 表示参与第 i 轮赌博,持有的钱所处阶段为 j (阶段数为0~2^m,共2^m + 1个阶段)并且采取最优策略时赢的概率。
本题是一个 倒推 的过程,初始化:dp[n][1 << m] = 1(即在最后一轮时,处在最后一个阶段,也就是资金已经大于100W)
状态转移方程:
dp[i][j] = max(dp[i][j], P * dp[i + 1][j + k] + (1 - P) * dp[i + 1][j - k] )。
0 <= k <= min(j, n - j) k是可以增长的阶段数
学到的方法:对于难题找不到入手点的时候,尽可能的选择最简单的情况开始分析,再看稍微复杂一点的,思考是否存在相应的联系,避免一点思绪都没有。
代码:
#include <iostream>
#include <algorithm>
using namespace std;
#define M 1000000
typedef long long ll;
int n;
int m, x;
double p;
double dp[2][1 << 15 + 1];
void slove()
{
n = 1 << m;
fill(dp[1], dp[1] + n, 0);
dp[1][n] = 1.0;
for (int k = 0; k < m; k++) //枚举第几轮
{
for (int i = 0; i <= n; i++) // 枚举所有阶段
{
double t = 0.0;
for (int j = 0;; j++) //可以增长的阶段数
{
//j是可以增长的阶段数,不能超过当前所处的阶段(此时为全押)
if (i + j > n || i - j < 0) break;
t = max(t, p*dp[(k + 1) & 1][i + j] + (1 - p)*dp[(k + 1) & 1][i - j]);//运用滚动数组
}
dp[k & 1][i] = t;
}
}
int ans = (ll)x * n / M; //判断在哪一个范围中
//根据m的奇偶性来判断处在哪一个数组中
printf("%.6f\n", dp[(m - 1) & 1][ans]);
}
int main()
{
int t;
cin >> t;
while (t--)
{
cin >> m >> p >> x;
slove();
}
system("pause");
return 0;
}
参考博客:
https://blog.youkuaiyun.com/liujc_/article/details/47256243