题目
题目背景
加特林轮盘赌是一个养生游戏.
题目描述
与俄罗斯轮盘赌等手枪的赌博不同的是,加特林轮盘赌的赌具是加特林。
加特林轮盘赌的规则很简单:在加特林的部分弹夹中填充子弹。游戏的参加者坐在一个圆桌上,轮流把加特林对着自己的头,扣动扳机一秒钟。中枪的自动退出,坚持到最后的就是胜利者。
我们使用的是2019年最新技术的加特林,他的特点是无需预热、子弹无限,每一个人,在每一回合,中枪的概率是完全相同的P_0P
0
。
每局游戏共有nn只长脖子鹿,从1长脖子鹿开始,按照编号顺序从小到大进行游戏,绕着圆桌不断循环。
游戏可能会循环进行多轮,直到场上仅剩下最后一只长脖子鹿时,游戏结束。
给出P_0P
0
和nn,询问kk号长脖子鹿最终成为唯一幸存者的概率P_kP
k
。
输入格式
仅一行三个数,P_0,n,kP
0
,n,k.
输出格式
一个浮点数P_{k} P
k
,误差应该小于10^{-8}10
−8
.(请保留更多位数的小数)
输入输出样例
输入 #1 复制
0.5 2 1
输出 #1 复制
0.33333333
输入 #2 复制
0.5 2 2
输出 #2 复制
0.66666667
输入 #3 复制
0.5 3 1
输出 #3 复制
0.23809524
输入 #4 复制
0.5 3 2
输出 #4 复制
0.28571429
说明/提示
对于10%的数据,n <= 100n<=100.
对于30%的数据,n <= 500n<=500.
对于另外20%的数据,k = nk=n.
对于100%的数据,1 <= k <= n <= 10^{4}, 0 <= P_0 <= 1.1<=k<=n<=10
4
,0<=P
0
<=1.
所有数据的时间限制为 1000ms 1000ms,空间限制为 256mb 256mb,可开启O2优化。
思路
令pw1[i]表示前(k-1)只长颈鹿自毙i次以内退役的概率,pw2[i]表示编号在k+1~n的长颈鹿自毙i次以内退役的概率。于是pw1[i]=s[i](k-1),pw2[i]=s[i](n-k),其中s[i]为一只长颈鹿自毙i次以内退役的概率,很容易得到s[i]=1-(1-p)i,因为一次中枪没有退役的概率为1-p,重复i次均退役则是(1-p)i,所以i次内退役的概率就是1-(1-p)i。因为题目中求的是小数,精度要求是10-8,所以枚举第k只长颈鹿在第i次中枪时退役且为最后一个退役的概率,把所有概率加起来就行了,可以做到线性复杂度,枚举到106即可(我写的是线性105也过了)。注意要特判n=1/k=1/k=n的情况。
代码
#include<bits/stdc++.h>
using namespace std;
double mi[10010];
double dp[2][10010];
int main(){
int n,m;
double k;
cin>>k>>n>>m;
if(k==0){
if(n==1)cout<<1<<endl;
else cout<<0<<endl;
return 0;
}
mi[0]=1;
for(int i=1;i<=n;i++){
mi[i]=mi[i-1]*(1.0-k);
}
dp[0][1]=1;
for(int i=2;i<=n;i++){
double sum=0;
for(int j=2;j<=i;j++){
sum+=(dp[0][j-1]*mi[i-j+1]);
}
dp[1][1]=k*sum/(1.0-mi[i]);
for(int j=2;j<=i;j++){
dp[1][j]=dp[1][j-1]*(1.0-k)+dp[0][j-1]*k;
}
for(int j=1;j<=i;j++){
dp[0][j]=dp[1][j];
}
}
printf("%0.8lf\n",dp[0][m]);
return 0;
}