Flipping Coins (概率DP)

题目链接
比赛地址

题目:

将N个相同的硬币排成一排,所有的硬币正面朝上向下放到桌子上,尾巴向上,然后执行K次操作每次取一枚硬币,抛出得到正面或反面。最后正面朝上的硬币可以拿走。问可以赢得的最大预期金额是多少?

思路:

期望公式 \(Ex = A * p(A)\) A表示是一个事件,P(A)是A发生的概率。这道题也是这样想的。最后正面朝上的个数num可能是1~n个。所以\(res = num * P(num)\)。问题变为求P(num),即最后num个硬币正面朝上的概率。

用dp求P(num)。我们用dp[i][j]表示i次操作后有j个正面朝上的概率,这样总共k次操作,所以P(num)=dp[k][num]。 如何转移呢?我们已知dp[0][0] = 1;

当正面朝上个数j小于n时,我们要优先对正面朝下的硬币先操作,每次操作后抛出硬币有可能正有可能背面向上。所以由dp[i][j]可以更新dp[i+1][j]和dp[i+1][j+1]。
      j < n 时,dp[i + 1][j + 1] += dp[i][j] * 0.5;
                dp[i + 1][j] += dp[i][j] * 0.5;
      
当正面朝上个数j等于n时,我们只能选正面向上的硬币操作,有正有负,所以可能会减少向上个数。所以do[i][n]可以更新dp[i+1][j]和dp[i+1][j-1];
      j == n 时 dp[i+1][j] += dp[i][j] * 0.5;
                dp[i+1][j-1] += dp[i][j] * 0.5;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<fstream>
using namespace std;
#define rep(i, a, n) for(int i = a; i <= n; ++ i);
#define per(i, a, n) for(int i = n; i >= a; -- i);
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e3 + 105;
const int mod = 998244353;
const double Pi = acos(- 1.0);
const int INF = 0x3f3f3f3f;
const int G = 3, Gi = 332748118;
ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
//

int n, k;
double dp[N][N];

int main()
{
    scanf("%d%d",&n,&k);
    memset(dp, 0,sizeof(dp));
    dp[0][0] = 1;
    for(int i = 0; i < k; ++ i){
        for(int j = 0; j < n; ++ j){
            dp[i + 1][j] += dp[i][j] * 0.5;
            dp[i + 1][j + 1] += dp[i][j] * 0.5;
        }
        dp[i + 1][n] += dp[i][n] * 0.5;
        dp[i + 1][n - 1] += dp[i][n] * 0.5;
    }
    double res = 0;
    for(int i = 1; i <= n; ++ i){
        res += i * dp[k][i];
    }
    printf("%.6lf\n",res);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/A-sc/p/12945009.html