开始有本金x,要进行p轮赌博,每一轮可以压一定数目的筹码。有p的概率获胜,如果获胜筹码翻一番,如果输了则筹码全部失去。
求m轮结束后手上钱超过一百万的概率。
由于本题的筹码可以是任何数字,所以是一个连续的变量,肯定不好求解,根据书上的分析,要想办法吧连续化为离散。
首先考虑自后一轮的情况:
1. 如果此时手上的钱少于50w的话,怎么样都不会赢,所以这个区间段概率为0;
2.如果手上钱超过100w的话,则坑定可以拿着100w回家(只要压上多出来的部分);
3.如果手上钱在50w到100w之间的话,全部押上。
下面考虑最后第二轮的情况:在50w和100w之间,如果在75w到100w之间,只需押上到100w的差值,赢了100w,输了至少有50w进入下一轮,没有必要多压;
如果在50w到75w之间,有两种方式:(1)押上到一百w的差值,赢了直接到100w,输了掉到50w以下(降级);
(2)押上超过50w的部分,保持在50w以上。
在0到50w之间,可以有同样的方式来划分区间。
所以,每一次赌博可以把一个区间划分成相等的两个区间,所以进行m次赌博就应该有2^m+1个区间来考虑。
由此,使用dp来解决本问题。程序中利用了数组的重复利用,节省的空间。
题源来自《挑战程序竞赛》第二版 132页,解法十分巧妙。
//
// 132_millionarie.cpp
// changlle
//
// Created by user on 1/8/16.
// Copyright (c) 2016 user. All rights reserved.
//
#include <iostream>
using namespace std;
const int MAX=5;
int M=3;
int X=600000;
double P=0.75;
double dp[2][(1<<MAX)+1];
double solve () {
int n=1<<M;
double *prv=dp[0], *nxt=dp[1];
memset(prv, 0,sizeof(n+1));
prv[n]=1.0;
for (int r=0;r<M;r++) {
for (int i=0;i<=n;i++) {
int jub=min(i,n-i);//若为n-i,则考虑只压n-i,没必要多冒风险。
//若为i,则要考虑全部押上的情况
double t=0.0;
for (int j=0;j<=jub;j++) {
t=max(t,P*prv[i+j]+(1-P)*prv[i-j]);
}
nxt[i]=t;
}
swap(prv,nxt);
}
int i=X*n/1000000;
return prv[i];
}
int main () {
cout<<solve()<<endl;
return 0;
}