Millionaire

题意:最开始你有x元钱,要进行M轮赌博。每一轮赢的概率为P,你可以选择赌与不赌,如果赌也可以将所持的任意一部分钱作为赌注(可以是整数,也可以是小数)。如果赢了,赌注将翻倍;输了赌注则没了。在M轮赌博结束后,如果你持有的钱在100万元以上,就可以把这些钱带回家。问:当你采取最优策略时,获得100万元以上的钱并带回家的概率是多少。

思路: 将最后一场赌局开始时的钱数分成三段,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;
} 


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值