• 【SPOJ KPGAME】A game with probability(概率DP+卡精度)


    点此看题面

    • (n)个石子,两人轮流掷硬币,正面朝上则取一颗棋子,反面朝上则不操作,取到最后一颗石子的人获胜。
    • 已知先手有(p)的概率掷出自己想要的面,后手有(q)的概率掷出自己想要的面,求先手获胜的概率。
    • (n<10^8)

    概率(DP)

    容易想到分别设(f_i,g_i)表示轮到先手/后手操作且还剩(i)颗石子时,先手获胜的概率。

    由于条件针对的是掷出自己想要的面,所以转移的时候我们首先要知道每个人会想要掷出哪一面。

    然后发现这显然取决于(f_{i-1})(g_{i-1}),若(g_{i-1}>f_{i-1})则二人都想要正面,否则二人都想要反面。

    方便起见,设新的(p,q)分别表示先手/后手掷出正面的概率,列出转移方程:

    [f_i=g_{i} imes(1-p)+g_{i-1} imes p\ g_i=f_i imes (1-q)+g_{i-1} imes q ]

    由于转移关系成环,我们把(g_i)代入到(f_i)的式子中,于是得到:

    [f_i=(f_i imes (1-q)+g_{i-1} imes q) imes(1-p)+g_{i-1} imes p\ f_i=frac{g_{i-1} imes q imes (1-p)+g_{i-1} imes p}{1-(1-q) imes (1-p)} ]

    然后代入到(g_i)的式子中就可以求出(g_i)了。

    (n)过大?

    由于这道题是保留小数输出的,而发现当(n)过大,答案基本上保持不变,所以我们完全可以将(n)向一个较小的值(例如(10^3))取(min)

    实测我一开始把n取太大反而被卡精度了。

    (O(T10^3))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000
    #define DB double
    using namespace std;
    int n;DB pp,qq,f[N+5],g[N+5];
    int main()
    {
    	RI Tt,i,j;DB p,q;scanf("%d",&Tt);W(Tt--)
    	{
    		for(scanf("%d%lf%lf",&n,&pp,&qq),n=min(n,N),g[0]=i=1;i<=n;++i)//将n向1e3取min
    			g[i-1]>f[i-1]?(p=pp,q=qq):(p=1-pp,q=1-qq),//根据g[i-1]与f[i-1]的大小关系确定二人的最优策略
    			f[i]=(f[i-1]*q*(1-p)+g[i-1]*p)/(1-(1-q)*(1-p)),g[i]=f[i]*(1-q)+f[i-1]*q;//转移
    		printf("%.6lf
    ",f[n]);
    	}return 0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    循环播放音乐
    在发板实现24位jpg和bmp图片用手划动显示上一张与下一张图片
    AnsiIO
    PosixIO
    java3
    java2
    java1
    ios音乐播放器demo
    添加文章查看连接
    elementary os变成mac风(笔记)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/SPOJ_KPGAME.html
Copyright © 2020-2023  润新知