zoukankan      html  css  js  c++  java
  • 洛谷 P5249 [LnOI2019]加特林轮盘赌 概率DP

    (f[n][k])(n) 个人第 (k) 个人胜利的概率。初始化 (f[n][1]=1)

    第一个人要想赢的话第一枪不能中,然后他就相当于成了 (n) 个人里的第 (n) 个,(f[n][1]=(1-P_0) imes f[n][n])

    对于其他人,如果第一个人中了,就转移到了 (f[n-1][k-1]),倘若没中,就转移到了 (f[n][k-1]),(f[n][k]=P_0 imes f[n-1][k-1] + (1-P_0) imes f[n][k-1])

    另外还有(displaystyle sum_{i=1}^n f[n][i]=1)

    手动高斯消元即可,具体方法可以看这

    #include<iostream>
    #include<cstdio>
    #define DB double
    using namespace std;
    int n, k, now = 1, last;
    DB p0, a, c, sa, C;
    DB f[2][20010];
    int main()
    {
    	cin >> p0 >> n >> k;
    	if (p0 == 0)return puts(n == 1 ? "1" : "0") == 2333;
    	f[1][1] = 1;
    	for (int i = 2; i <= n; ++i)
    	{
    		a = 1; c = sa = C = 0; last = now, now ^= 1;
    		for (int j = 2; j <= i; ++j)a *= (1 - p0), sa += a, c = (1 - p0) * c + p0 * f[last][j - 1], C += c;
    		f[now][1] = (1 - C) / (sa + 1);
    		for (int j = 2; j <= i; ++j)f[now][j] = p0 * f[last][j - 1] + (1 - p0) * f[now][j - 1];
    	}
    	printf("%.10f", f[now][k]);
    	return 0;
    }
    

    ...是不是有点迷糊?,因为这题是实数输出,我们可以模拟的,次数一定后误差就会小到忽略不计(其他概率题也可以试试这种方法)

    具体方法就是枚举这个人在每一轮取胜的概率(枚举到 (10^5) 就够了),最后加起来即可。

    附上treAKer巨佬的主函数。

    int main()
    {
    	std::cin >> p; n = read(); k = read();
    	if(n == 1) return puts("1"), 0;
    	for(int i = 1; i <= 1e5; ++ i) s[i] = 1 - ksm(1-p,i);
    	for(int i = 1; i <= 1e5; ++ i) a[i] = ksm(s[i], k - 1), b[i] = ksm(s[i], n - k);
    	for(int i = 1; i <= 1e5; ++ i)
    	{
    		if(k == n) ans += a[i] * (s[i] - s[i - 1]);
    		else if(k == 1) ans += b[i - 1] * (s[i] - s[i - 1]);
    		else ans += a[i] * b[i - 1] * (s[i] - s[i - 1]);
    	}
    	printf("%0.9f",ans);
    	return 0;
    }
    
  • 相关阅读:
    POJ2406【KMP-next数组】
    POJ2752【KMP-next数组含义】
    POJ3696【欧拉函数+欧拉定理】
    POJ3692【二分匹配】
    POJ3697【BFS】
    CodeForces599D【数学】
    CodeForces599C【贪心】
    HDU1829【种类并查集】
    HDU3038【种类并查集】
    POJ1182【种类并查集】
  • 原文地址:https://www.cnblogs.com/wljss/p/12601304.html
Copyright © 2011-2022 走看看