2008 APAC local onsites Problem C. Millionaire

2008 APAC local onsites Problem C. Millionaire

參考博客:https://blog.csdn.net/a2459956664/article/details/51106591

 

Problem C. Millionaire
This contest is open for practice. You can try every problem as many times as you like, though we won't keep track of which problems you solve. Read the Quick-Start Guide to get started.
Small input
13 points     
Large input
16 points     

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 to determine 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 any part 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 your winnings (this time the amount is rounded down to whole dollars) only if you have accumulated $1000000 or more. Otherwise you get nothing.

Given M, P and X, determine your probability of winning at least $1000000 if you play optimally (i.e. you play so that you maximize your chances 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

Sample

Input      
Output
2
1 0.5 500000
3 0.75 600000

    Case #1: 0.500000
Case #2: 0.843750
 

In the first case, the only way to reach $1000000 is to bet everything in the single round.

In the second case, you can play so that you can still reach $1000000 even if you lose a bet. Here's one way to do it:

    You have $600000 on the first round. Bet $150000.
    If you lose the first round, you have $450000 left. Bet $100000.
    If you lose the first round and win the second round, you have $550000 left. Bet $450000.
    If you win the first round, you have $750000 left. Bet $250000.
    If you win the first round and lose the second round, you have $500000 left. Bet $500000.
 

题意:最开始你有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<cstring>
using namespace std;

typedef long long ll;
const int MAXN = 15;

double dp[2][(1<<MAXN)+1];
int main(){

    int M,X;
    double P;

    scanf("%d %lf %d",&M,&P,&X);

    int n = 1<<M;
    double *pre = dp[0];
    double *nxt = dp[1];

    memset(pre,0,sizeof(double)*(n+1));

    pre[n] = 1.0;

    for(int r=0;r<M;r++){
        for(int i=0;i<=n;i++){
            int step = min(i,n-i);
            double t =0.0;
            for(int j=0;j<=step;j++){
                t = max(t,pre[i+j]*P+pre[i-j]*(1-P));
            }
            nxt[i] = t;
        }
        swap(pre,nxt);
    }
    int i=(ll)X*n/1e6;
    printf("%.6f\n",pre[i]);

    return 0;
}

總結:

1.首先我們設置了一個狀態數組dp[i][j],這個數組對應的表示的是,在第i輪賭博時,對應的所持有的錢X時,所对应的最优的赚的100万的概率。

2.对应的状态转移公式为:dp[i][j] = max(P * dp[i + 1][j + k] + (1 - P) * dp[i + 1][j - k]),没错,是从后往前的递推,因为我们一开始就知道如果大于等于100万的时候的概率为1,这是就由dp[M-1][n]表示(其中M-1 表示轮数下标,n表示所持有的钱的模块为100万的模块),那么对应的前 1到M-2 轮的概率,就要在这最后一轮的基础上进行迭代。

3.可以看到,dp[i + 1][j + k] 表示的是当前的钱数模块为j时,赢得k模块钱的状态的概率(就是你当前有j模块的钱,但是你可以在0到j-1 的模块里作为赌注,如果你赢了,那么就能获得j+k 的钱款,所以step = min(i,n-i),因为你不可能押注 超过你手里钱款的钱,所以相应的,你也可能输掉 就有dp[i + 1][j-k] 的状态),所以我们给予从后往前的递推,我们上一轮的概率,就能递推出这一轮的对应的赚的100万的概率。

4.那么最终 dp[i][j] = max(P * dp[i + 1][j + k] + (1 - P) * dp[i + 1][j - k]) 就可以理解为,我在第i轮的时候,如果我有j模块的钱,那么我有P的概率赢得k 模块的钱,有1-P的概率输掉k模块的钱,所以我们最终的在第i轮的时候,有j模块的钱,要想赢得100万,那么这个时候的最大的概率就是再乘上后续的状态的最大赢得100万的概率,所以就有max(P * dp[i + 1][j + k] + (1 - P) * dp[i + 1][j - k])

5. 那么我们的目标就是最终确定dp[0][X×n/1e6]的状态的时候所能获得的最大概率就是所求概率。对应的因为每次迭代都只用到上一层的状态,所以用了滚动数组。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值