BZOJ 2318 Spoj4060 game with probability Problem 概率DP

本文介绍了一种计算投币游戏中Alice获胜概率的方法。通过建立递推公式来确定不同状态下玩家的胜率,并考虑了是否拿走石子的最优策略。提供了一个C++实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述 Description
Alice和Bob在玩一个游戏。有n个石子在这里,Alice和Bob轮流投掷硬币,如果正面朝上,则从n个石子中取出一个石子,否则不做任何事。取到最后一颗石子的人胜利。Alice在投掷硬币时有p的概率投掷出他想投的一面,同样,Bob有q的概率投掷出他相投的一面。

输入描述 Input Description
第一行一个正整数t,表示数据组数。

对于每组数据,一行三个数n,p,q。

输出描述 Output Description
对于每组数据输出一行一个实数,表示Alice胜利的概率,保留6位小数。

样例输入 Sample Input
1
1 0.5 0.5

样例输出 Sample Output
0.666667

数据范围及提示 Data Size & Hint
1<=t<=50,0.5<=p,q<=0.99999999,1<=n<=999999991<=t<=50,0.5<=p,q<=0.99999999,1<=n<=99999999

Solution

f[i]f[i]为还剩ii个石子时轮到Alice,Alice的胜率,g[i]为还剩ii个石子时轮到Bob,Alice的胜率
假设每一次都是拿走石头最优,则会有
f[i]=pg[i1]+(1p)g[i]
g[i]=qf[i1]+(1q)f[i]g[i]=q∗f[i−1]+(1−q)∗f[i]
将两式化为可以递推的形式,使f[i]f[i]f[i1]f[i−1]g[i1]g[i−1]转移,g[i]g[i]同理
最终化为:

f[i]=pg[i1]+(1p)qf[i1]p+qpqf[i]=p∗g[i−1]+(1−p)∗q∗f[i−1]p+q−p∗q

g[i]=qf[i1]+(1q)pg[i1]p+qpqg[i]=q∗f[i−1]+(1−q)∗p∗g[i−1]p+q−p∗q

好的,但是有个问题,就是并不一定每次都是拿走石头最优,万一这次拿走石头后对方马上就赢了呢?
所以说有时候可能会选择不拿走石子
不拿走石子时则会有:
f[i]=pg[i]+(1p)g[i1]f[i]=p∗g[i]+(1−p)∗g[i−1]
g[i]=qf[i]+(1q)f[i1]g[i]=q∗f[i]+(1−q)∗f[i−1]

忽然发现不就把(p,1p),(q,1q)(p,1−p),(q,1−q)分别对调一下么

什么时候拿走石头最优呢?若成功拿走石子的收益大于不拿走石子的收益者拿走,否则不拿走
对比f[i1],g[i1]f[i−1],g[i−1]即可

代码如下:

#include <bits/stdc++.h>
using namespace std;
int n;
double p,q;
double f[10005],g[10005];
void work() {
    scanf("%d%lf%lf",&n,&p,&q);
    n=min(n,10000);
    g[0]=1;
    for(int i=1;i<=n;i++) {
        if(f[i-1]>g[i-1]) { p=1.0-p;q=1.0-q; }
        double d=p+q-p*q;
        f[i]=(p*g[i-1]+(1-p)*q*f[i-1])/d;
        g[i]=(q*f[i-1]+(1-q)*p*g[i-1])/d;
        if(f[i-1]>g[i-1]) { p=1.0-p;q=1.0-q; }
    }
    printf("%.6lf\n",f[n]);
}
int main() {
    int t;
    scanf("%d",&t);
    while(t--) {work();}
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值