Problem
You have been invited to the popular TV show “Would you like to be a millionaire?”.Of course you would!
The rules of the show are simple:
Before the game starts, the host spins a wheel of fortune todetermine P, the probability of winning each bet.
You start out with some money: X dollars.
There are M rounds of betting. In each round, you can bet anypart ****of your current money, including none of it or all of it. The amount is not limited to whole dollars or whole cents.
If you win the bet, your total amount of money increases by the amount you bet. Otherwise, your amount of money decreases by the amount you bet.**
After all the rounds of betting are done, you get to keep yourwinnings (this time the amount is rounded down to whole dollars) only if you have accumulated
1000000ormore.Otherwiseyougetnothing.GivenM,PandX,determineyourprobabilityofwinningatleast
1000000 if you play optimally (i.e. you play so that you maximize yourchances of becoming a millionaire).
Input
The first line of input gives the number of cases, N.
Each of the following N lines has the format “M P X”, where:
M is an integer, the number of rounds of betting.
P is a real number, the probability of winning each round.
X is an integer, the starting number of dollars.
Output
For each test case, output one line containing “Case #X: Y”, where:
X is the test case number, beginning at 1.
Y is the probability of becoming a millionaire, between 0 and 1.
Answers with a relative or absolute error of at most 10-6 will be considered correct.
Limits
1 ≤ N ≤ 100
0 ≤ P ≤ 1.0, there will be at most 6 digits after the decimal point.
1 ≤ X ≤ 1000000
Small dataset
1 ≤ M ≤ 5
Large dataset
1 ≤ M ≤ 15
思路:
将最后一场赌局开始时的钱数分成三段,0~500000,500000~1000000,1000000以上。第一段赢得概率为0,第二段为P,第三段为1。
依此类推,第一局分段最多,为2^M+1。因此,可将钱数分为2^M+1段,任何一局每一段内的赢率相同。从最后一局开始向前递推,每局开始时钱数为j段,结束时为j+k(赢)或j-k(输),概率为P和1-P。
动态方程:dp[i][j] = max(dp[i][j], P*dp[i+1][j+k] + (1 - P)*dp[i+1][j-k])
dp[i][j]为第i场赌局开始时钱数为j段的情况下赢的概率。
由于每一次概率的更新只与下一场赌局(i+1)有关,所以可以设两个一维数组交换进行,可以节省内存。
动态方程:st[j] = max(st[j], P*en[j + k] + (1 - P)*en[j - k])
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
double en[1<<15 + 2]; //第i场结束时拥有的钱数在j区间
double st[1<<15 + 2]; // 第i场开始时拥有的钱数在j区间
int main()
{
int M, X, N;
double P;
scanf("%d", &N);
for(int a = 1; a <= N; a++){
scanf("%d %lf %d", &M, &P, &X);
int n = 1<<M;
//n = 2^M,将1000000分成n+1个区:0~1000000/n,1000000/n~2*1000000/n...(n-1)*1000000/n~1000000,000000以上 若钱数>=1000000可直接带走,概率1
memset(en, 0, sizeof(en));
memset(st, 0, sizeof(st));
en[n] = 1; //若最后一局结束时钱数在n区间则必赢
for(int i = M; i > 0; i--){ //第i场赌赛(倒着算)
for(int j = 0; j <= n; j++){ //本场赌局开始时我拥有的钱数在j区间,这种情况下我最终能赢概率为st[j]
int temp = min(j, n - j);
//temp为本轮赌局结束时我最多拥有的钱数
//保证2*j <= n,因为钱数到n区间可直接拿走,超出没有意义,若输掉赌局可能失去更多钱,不是最优解
st[j] = 0.0;
for(int k = 0; k <= temp; k++){ //下一轮赌局拿出k区间的钱数参赌
st[j] = max(st[j], P*en[j + k] + (1 - P)*en[j - k]);
//若赢,钱数 = j - k + k*2 = j + k
//若输,钱数 = j - k
}
}
swap(en, st); //本场赌局开始的钱数和上一场赌局结束的钱数相同
}
int i = (long long)X*n/1000000; //最初我拥有的钱数在i区间
printf("Case #%d: %.6f\n", a, en[i]);
}
return 0;
}