挑战程序设计竞赛:Millionaire

题目大意

在这里插入图片描述

解题思路

离散化思想: 首先对金钱分阶段进行离散化,如果只有一轮的情况下,容易知道有三个阶段:
在这里插入图片描述
当有两轮的时候,也可以知道,有五个阶段:
在这里插入图片描述
当轮数为 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 &lt; = k &lt; = m i n ( 2 M + 1 − j , j ) 1&lt;=k&lt;=min(2^M+1-j,j) 1<=k<=min(2M+1j,j), 这里不考虑 k &gt; = 2 M + 1 − j k&gt;=2^M+1-j k>=2M+1j,是因为没必要花更多的钱去达到更少的钱能够达到的阶段。
    • 对(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 1P, 会转移到 d p [ i + 1 ] [ j − k ] dp[i+1][j-k] dp[i+1][jk]阶段。
    • 由于赌赢赌输互斥,因此由全概率公式,有转态转移方程: d p [ i ] [ j ] = m a x { P ∗ d p [ i + 1 ] [ j + k ] + ( 1 − P ) ∗ d p [ i + 1 ] [ j − k ] ∣ 1 &lt; = k &lt; = 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&lt;=k&lt;=min(2^M+1-j,j)\} dp[i][j]=max{Pdp[i+1][j+k]+(1P)dp[i+1][jk]1<=k<=min(2M+1j,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 &lt; = m o n e y &lt; 500000 0 &lt;= money &lt; 500000 0<=money<500000时,概率为0, 即阶段 1 &lt; = k &lt; = 2 M − 1 1 &lt;=k&lt;=2^{M-1} 1<=k<=2M1.
    • 500000 &lt; = m o n e y &lt; 1000000 500000&lt;=money&lt;1000000 500000<=money<1000000时, 概率为P,即阶段 2 M − 1 &lt; k &lt; = 2 M 2^{M-1}&lt;k&lt;=2^M 2M1<k<=2M.
    • 概率为 P P P, 当 m o n e y &gt; = 1000000 money&gt;=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;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值