2008 APAC local onsites C Millionaire (离散化+期望dp)

题面

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

想法与思路

说实话,这题刚开始看的时候毫无思路。虽然最优策略明确指向了贪心和dp,但是这题的贪心策略明显不是很好像,作为连续的点,暴力穷举显然也是失效的。在dp方面,还是没有想到好的办法,最后还是看了题解。简单讲一下思路吧,当我们遇到这些一时间找不到思路的dp时,可以先考虑举几个例子试试看,这里我们考虑一下最后一轮的情况,很明显,这里有三种情况,当金额>1000000时,我们之间可以拿钱回家,金额>500000的时候,我们就可以在最后一轮的赌博之中放手一搏了,这时候我们可以all in,因为投放较少的金额,最后如果成功依旧是拿钱回家,所以直接all in便于考虑问题,当金额小于500000时,显然我们没有搏一搏的机会了,这些钱都不会被你带走。接下来我们考虑倒数第二轮,综合前面的情况,我们发现倒数第二轮的时候,我们会有5种情况,最后我们发现,第i轮的情况有2的m次幂+1种情况,当然这里是反向穷举,需要大家注意一下。然后我们就可以去考虑dp方程的问题,一个状态的最优概率值,显然是可以转移为投资失败,从而变成i-j的状态,或者投资成功变成i+j的状态,这里我们取和就可以了。关于下面的代码实现,稍微解释一下几个小点,pre和bes是基于滚动数组的思路实现的,就是一个迭代更新的过程,然后从i个状态的转移跨度的话,是取(i,n-i)的,因为你不可能会去转移到n之外。

代码实现

	#include<iostream>
	#include<cstring>
	#include<cmath>
	#include<queue>
	#include<vector> 
	#include<algorithm>
	using namespace std;
	const int maxn= 10005;
	const int N=1e6+5;
	const int inf=0x3f3f3f3f;
	typedef long long ll;
	int x,m;
	double p;
	double dp[2][(1<<15)+1];
	void solve () {
		int n=1<<m;
		double *pre=dp[0];
		double *bes=dp[1];
        memset (pre,0,sizeof(double) *(n+1));
        pre[n]=1.0;
        for (int i=0;i<m;i++) {
			for (int j=0;j<=n;j++) {
				int step=min (j,n-j);
				double t=0.0;
				for (int k=0;k<=step;k++) {
					t=max(t,pre[j+k]*p+(1-p)*pre[j-k]);
				}
				bes[j]=t; 
			}
			swap(pre, bes);
		}
		int l=(ll)x*n/1000000;	
		printf ("%.6lf",pre[l]);
	}
	int main() {
        cin>>m>>p>>x;
		solve ();

		return 0;
	}

posted @ 2020-06-11 12:29  Luglucky  阅读(162)  评论(0编辑  收藏  举报