Millionaire (2008 APAC local onsites C)
假设游戏开始时,你有X元钱,可进行M轮竞猜。每一轮可以将所持的任意
一部分钱作为赌注,赌注不光是整数,也可以是小数。一分钱不押或全押
都可以。每一轮都有P的概率可以赢,赢了赌注就会翻倍,输了赌注就没了,
如果你最后持有1000,000元以上的钱就可以将这些钱带回家。请计算当你
采取最优策略时,获得1000,000元以上的钱并带回家的概率。
限制条件
0<= P <= 1.0
1<= X <= 1000,000
Small
1<= M <= 5
Large
1<= M <= 15
开始分析这个题目时,看到最优策略时,脑子里就闪过动态规划和贪心算法,
动态规划和贪心算法是解决最优解问题的两大常用策略。
遇到问题,我们首先应该先从简单场景考虑,然后再推到复杂场景,最后再
推出通用规律,也就是递推公式,或表达式。
以此题为例,我们以最简单的一轮竞猜为例,那么情形就比较清楚了,当我们
手里有超过1000,000元钱时,我们赢钱回家的概率为1,而根据赢钱翻倍的规则,
如果手里刚好超过[500,000元-1000,000)元钱,刚好是个左闭右开区间,在这种情况
下要想赢钱回家,必须将手里的钱押一轮,才有可能赢钱回家,假设押的钱为A,
押完后剩下的钱为B,那么:
A + B = X 2A + B >= 1000,000 500,000<= X <1000,000可推出 :
1<= A < 1000,000 0 <= B < 500,000无论A具体为多少元钱,这一轮要求必须赌赢才能赢钱回家,而赢的概率为P,故
这种情形下,赢钱回家的概率为P。
如果手里的钱不足500,000时,即便是把所有的钱都押上,且赌赢了,翻倍后也不足
1000,000,所以这种情况下赢钱回家的概率为0。
相对于只有一轮时的情形,当还有两轮时,情况就复杂多了,其实仔细分析可以人为是
在只有一轮时,每种情况再一分为二,当然手里超过1000,000元钱的情况就不用再一分为
二了,所以只剩下两轮时,共有5种情况。
由上面简单情形的分析,我们可以推算出当M大于2时,共有2^M+1种情况。
再仔细分析,会发现动态规划适用于解决该问题,动态规划通常可以分为4个步骤:
描述最优解结构 递归定义最优解的值 按自底向上的方式计算最优解的值 由计算出的结果构造一个最优解下面省去了动态规划详细分析过程,直接上程序,引用挑战程序设计大赛
#include "stdafx.h" #include <iostream> using namespace std; int min(int a, int b) { if(a <= b) return a; else return b; } double max(double a, double b) { if(a >= b) return a; else return b; } int _tmain(int argc, _TCHAR* argv[]) { int M;//竞猜轮数 int X;//初始资金 double P = 0.0;//竞猜成功的概率 cout<<"输入竞猜轮数"<<endl; cin>>M; cout<<"输入初始资金"<<endl; cin>>X; cout<<"成功概率"<<endl; cin>>P; double dp[2][(1<<15)+1]; 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 jub = min(i,n-i); double t = 0.0; for(int j =0; j <= jub; j++) { t = max(t,P*pre[i+j]+(1-P)*pre[i-j]); } nxt[i] = t; } swap(pre,nxt); } int i = (long long)X*n/1000000; cout<<pre[i]<<endl; return 0; }
挑战程序设计 Millionaire
最新推荐文章于 2019-07-26 12:26:15 发布