题目大意
解题思路
离散化思想: 首先对金钱分阶段进行离散化,如果只有一轮的情况下,容易知道有三个阶段:
当有两轮的时候,也可以知道,有五个阶段:
当轮数为
M
M
M时,有
2
M
+
1
2^M+1
2M+1个状态
- 证明:当有 M M M轮时,对 M M M轮的输赢,共有 2 M 2^M 2M中情况。 我们对每一种情况分配一个金钱阶段,在这种情况下,处于这个金钱阶段里的金钱最终能够到达 1000000 1000000 1000000。比如对全输的情况,那么对应的金钱阶段就是大于等于 1000000 1000000 1000000元。还有一种情况就是无论如何都到达不了 1000000 1000000 1000000元,因此共有 2 M + 1 2^M+1 2M+1种状态。又因为每次赌博是翻倍的,因此每个阶段的覆盖的金钱区间长度是一样的。
动态规划过程:
- 定义 d p [ i ] [ j ] dp[i][j] dp[i][j]: 第 i i i轮赌博时,拥有的钱在阶段 j j j能走人的最大概率。
- 目标: d p [ 1 ] [ s t a g e ( X ) ] : dp[1][stage(X)]: dp[1][stage(X)]:第1轮赌博时,拥有的钱为 X X X, 其阶段为 s t a g e ( X ) stage(X) stage(X).此时获胜的最大概率。
- 状态转移: 对状态
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]来说,(1)赌徒可以拿出横跨
k
k
k个阶段的钱来赌博。(2)可能赌输也可能赌赢。
- 对(1) 有 1 < = k < = m i n ( 2 M + 1 − j , j ) 1<=k<=min(2^M+1-j,j) 1<=k<=min(2M+1−j,j), 这里不考虑 k > = 2 M + 1 − j k>=2^M+1-j k>=2M+1−j,是因为没必要花更多的钱去达到更少的钱能够达到的阶段。
- 对(2): 1、赌赢: 概率为 P P P, 会转移到 d p [ i + 1 ] [ j + k ] dp[i+1][j+k] dp[i+1][j+k]状态。2、赌输: 概率为 1 − P 1-P 1−P, 会转移到 d p [ i + 1 ] [ j − k ] dp[i+1][j-k] dp[i+1][j−k]阶段。
- 由于赌赢赌输互斥,因此由全概率公式,有转态转移方程:
d
p
[
i
]
[
j
]
=
m
a
x
{
P
∗
d
p
[
i
+
1
]
[
j
+
k
]
+
(
1
−
P
)
∗
d
p
[
i
+
1
]
[
j
−
k
]
∣
1
<
=
k
<
=
m
i
n
(
2
M
+
1
−
j
,
j
)
}
dp[i][j] = max\{P*dp[i+1][j+k]+(1-P)*dp[i+1][j-k] \quad | 1<=k<=min(2^M+1-j,j)\}
dp[i][j]=max{P∗dp[i+1][j+k]+(1−P)∗dp[i+1][j−k]∣1<=k<=min(2M+1−j,j)}
前一项 d p [ i + 1 ] [ j + k ] dp[i+1][j+k] dp[i+1][j+k]是因为拿覆盖 k k k阶段出来赌,赢了翻倍即多出 k k k阶段钱,即到了 j + k j+k j+k阶段。
- 更新策略: i i i从大到小更新。(实现时采用滚动数组交替更新)
- 初始化: 考虑最后一轮:
- 当钱 0 < = m o n e y < 500000 0 <= money < 500000 0<=money<500000时,概率为0, 即阶段 1 < = k < = 2 M − 1 1 <=k<=2^{M-1} 1<=k<=2M−1.
- 当 500000 < = m o n e y < 1000000 500000<=money<1000000 500000<=money<1000000时, 概率为P,即阶段 2 M − 1 < k < = 2 M 2^{M-1}<k<=2^M 2M−1<k<=2M.
- 概率为 P P P, 当 m o n e y > = 1000000 money>=1000000 money>=1000000时,概率为1,即阶段 2 M + 1 2^M+1 2M+1.
- 复杂度: O ( M 2 M ) O(M2^M) O(M2M)
代码
#include<iostream>
#include<stdio.h>
using namespace std;
const double thread = 1000000;
const int MAXM = 16;
double dp[2][1 << MAXM];
double X;
int M;
double P;
int main()
{
while(cin >> M >> P >> X)
{
int m = (1 << M) + 1; // 1 - 2^M + 1共 2^M+1个状态
double per_range = thread / (m-1);
for(int i=1; i<=m/2; i++)
dp[M%2][i] = 0;
for(int i=m/2; i<=m-1; i++)
dp[M%2][i] = 0.5;
dp[M%2][m] = 1.0;
for(int i=M-1; i>=1; i--)
{
for(int j=1; j<=m; j++)
{
for(int k=1; k<=min(j, m-j); k++)
{
dp[i%2][j]= max(dp[i%2][j], P*dp[1-i%2][j+k]+(1-P)*dp[1-i%2][j-k]);
}
}
}
printf("%.6f\n", dp[1%2][(long long)X*m/1000000+1]);
}
return 0;
}