半夜睡不着,起来看看书,就看到了这题,费了大半个小时才看明白,然后不困了Orz。
书上都有代码,但是为什么我再敲一遍。如果没明白我是不会抄一遍的,那样没有意义。
看到能给我启发的题目,我觉得还是记录下来比较好,毕竟0-1背包,自己还没有写过小数版的Orz。
本来也不怎么会说,还不如课本说的清楚。直接截图吧。
状态转移方程:
从后往前推:dp[i][j] 表示第还剩i轮次,当前的金币数为j。
设第i次的选择是拿出k当作赌注,那么输了概率是1-p,最后的钱是j-k,如果赢了,最后的钱是j+k,
那么作为金钱数j的当前轮次所能赢的最终钱的概率的最优选择是最合适的k,让他的概率最高。
dp[i][j] =max{ dp[i-1][j+k]*p + dp[i-1][j-k]*(1-p)}
使用滚动数组来节省内存,不过不用也应该是可以的,M最大是16
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; typedef long long LL; const int MAXM = 16; double dp[2][1 << MAXM | 1]; int M, X; double P; void solve() { memset(dp, 0, sizeof(dp)); int n = 1 << M; dp[0][n] = 1.0; for(int r = 1; r <= M; r++) { for(int i = 0; i <= n; i++) { double t = 0.0; int duzhu = min(i, n - i); for(int j = 0; j <= duzhu; j++) { t = max(t, P * dp[!(r & 1)][i + j] + (1 - P) * dp[!(r & 1)][i - j]); } dp[r & 1][i] = t; } } int x = (LL)X * n / 1000000; printf("%.6f\n", dp[M & 1][x]); } int main() { scanf("%lf%d%d", &P, &X, &M); solve(); return 0; }
时间: 2024-10-13 10:21:14