设(f_i)表示有(i)元钱时的获胜概率,那么我们有:(f_0=0,f_n=1,f_m=maxlimits_{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),最多(10^4)层即可。
注意到答案实际上只与(frac mn)有关,因此我们考虑用(f(x))代替(f(nx))来进行计算。
那么也就是:
考虑将(x)展开为二进制,若(x=sumlimits_{ige0}2^{-e_i}),那么(f(x)=sumlimits_{ige0}q^ip^{e_i}),其中(q=frac{1-p}p)。
我们可以推出(forall xge yge 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(ainmathbb N,bin[0,2^e))),那么可行的(d)的取值就是((2^epm 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
%d
",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
",cal(n,m,0)),getans(n,m);
printf("%d
",(int)ans.size());
for(int x:ans) printf("%d ",x);
}