JAGSpring2015C Casino

Link

\(f_i\)表示有\(i\)元钱时的获胜概率,那么我们有:\(f_0=0,f_n=1,f_m=\max\limits_{x=1}^{\min(m,n-m)}pf_{m+x}+(1-p)f_{m-x}\)

\(p=0\):获胜概率为\(0\),可行的决策集合为\([1,m]\)

\(p=1\):获胜概率为\(1\),可行的决策集合为\([1,m]\)

\(p=\frac12\):获胜概率为\(\frac mn\),可行的决策集合为\([1,\min(m,n-m)]\)

\(p>\frac12\)

直觉告诉我们此时选择\(x=1\)是最优的。因为概率对我们是有利的,所以时间拖得越长越有利。
不难验证\(x=1\)也是唯一的最优策略。
那么此时的\(f\)就是一个经典的赌徒输光模型,令\(q=\frac{1-p}p\),那么\(f_m=\frac{1-q^m}{1-q^n}\)

\(p<\frac12\)

直觉告诉我们此时\(x\)应该尽可能大,也就是说\(x=\min(m,n-m)\)
事实上这确实是最优的策略,但是还存在其它的最优策略。
首先代入得到:

\[f_m= \begin{cases} pf_{2m}&2m\le n\\ p+(1-p)f_{2m-n}&2m>n \end{cases} \]

然后我们就可以利用这个式子暴力迭代计算\(f_m\),最多\(10^4\)层即可。
注意到答案实际上只与\(\frac mn\)有关,因此我们考虑用\(f(x)\)代替\(f(nx)\)来进行计算。
那么也就是:

\[f_x= \begin{cases} pf_{2x}&2x\le 1\\ p+(1-p)f_{2x-1}&2x>1 \end{cases} \]

考虑将\(x\)展开为二进制,若\(x=\sum\limits_{i\ge0}2^{-e_i}\),那么\(f(x)=\sum\limits_{i\ge0}q^ip^{e_i}\),其中\(q=\frac{1-p}p\)
我们可以推出\(\forall x\ge y\ge 0,f(x+y)\ge f(x)+qf(y)\)。(证明比较复杂,此处省略)
那么我们现在需要做的就是找到取等号的充要条件,先将\(x,y\)写成\(\frac{m+d}n,\frac{m-d}n\)的形式。
\(\frac mn\)二进制展开,考虑其中的每一位01\(\frac mn=a2^{e+2}+2^e+b(a\in\mathbb N,b\in[0,2^e))\),那么可行的\(d\)的取值就是\((2^e\pm b)n\)

#include<set>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
const double eps=1e-10;
int n,m;double p;std::set<int>ans;
double cal(int n,int m,int d)
{
    if(!n||d>10000) return 0;
    if(n==m) return 1;
    int q=std::min(n,m-n);
    return p*cal(n+q,m,d+1)+(1-p)*cal(n-q,m,d+1);
}
void getans(int n,int m)
{
    for(;n&&n<m;m/=2,n%=m)
    {
	ans.insert(std::min(n,m-n));
	if(m&1) break;
    }
}
void print(double ans,int c)
{
    printf("%.10lf\n%d\n",ans,c);
    if(c<=200) for(int i=1;i<=c;++i) printf("%d ",i);
    else
    {
	for(int i=1;i<=100;++i) printf("%d ",i);
	for(int i=c-99;i<=c;++i) printf("%d ",i);
    }
    exit(0);
}
int main()
{
    scanf("%lf%d%d",&p,&n,&m),p/=100;
    if(fabs(p)<eps||fabs(p-1)<eps) print(p,n);
    if(fabs(p-0.5)<eps) print(1.0*n/m,std::min(n,m-n));
    if(0.5-eps<p) print((1-pow((1-p)/p,n))/(1-pow((1-p)/p,m)),1);
    printf("%.10lf\n",cal(n,m,0)),getans(n,m);
    printf("%d\n",(int)ans.size());
    for(int x:ans) printf("%d ",x);
}

猜你喜欢

转载自www.cnblogs.com/cjoierShiina-Mashiro/p/12556682.html
今日推荐