Gym101606F Flipping Coins

题目链接

原文链接

题意:
一个愉快简单的游戏:有N个相同的硬币,都是正面朝下,反面朝上。我们每次拾起一枚硬币,抛,抛后的结果(正面/反面朝上)取代硬币原来的朝向。抛K次,我们最后可能使得所有的硬币都正面朝上。
本质上说,这是一个资本家的游戏,我们必须要采取最佳的玩法去赢得最多的硬币。在所有的策略和结果的组合中,通过最佳的玩法,我们能够获得的硬币的最大期望(平均值)是多少?

输入:
一行两个数N,K(1 <= N, K <= 400)。N是硬币的数量,K是抛的次数。

输出:
输出一个期望值(实数),表示我们最后可能得到的正面朝上的硬币的数量。输出结果精确到10^-6。

做法:
换句话说,题意也就是:给N个硬币,开始均反面朝上。每次挑出其中一个抛,连续K次,求正面朝上的最大数学期望。

由于是求最大数学期望,所以每次抛硬币即要优先选择反面硬币。

(1)暴力的想法
以样例3为例,我们可以得到下面的结果,注意优先选择反面的硬币。
这里写图片描述
但是1 <= N, K <= 400,如果抛400次的话,我们就会有2^400种可能,而2^400近似于2.58*10^120,显然暴力法是不可行的,而且考虑优先选择反面,情况也会变得很复杂。

(2)动态规划
优先选择反面硬币,所以只有两种挑选硬币的情况:

1.正面数量为 0 ~ n-1 ,选择反面硬币抛,抛出结果正面数量比原本 +1不变

2.正面数量为 n,只能够选择正面硬币抛,抛出结果正面数量比原本 -1不变

设 dp[i][j] 表示: 第 i 次抛硬币后, j 个硬币正面朝上的概率

1.当 j < n 时,dp[i][j]的概率一分为二,各给dp[i+1][j]和dp[i+1][j+1],即

dp[i + 1][j] += dp[i][j] * 0.5;
dp[i + 1][j + 1] += dp[i][j] * 0.5;

2.当 j == n 时,dp[i][j]的概率一分为二,各给dp[i+1][j]和dp[i+1][j-1],即

dp[i + 1][n] += dp[i][n] * 0.5;
dp[i + 1][n - 1] += dp[i][n] * 0.5;

如此即可求出n个硬币抛k次的各个正面朝上的概率,最后求数学期望即可。

当n = 2,k = 3时,dp转移表格:
这里写图片描述

#include <stdio.h>
#include <string.h>

double dp[405][405];

int
main() {
    int n, k, i, j;
    double ans;

    while( scanf("%d %d", &n, &k) != EOF ) {
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for( i = 0; i < k; i++ ) {
            for( 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;
        }
        ans = 0;
        for( i = 1; i <= n; i++ ) {
            ans += i * dp[k][i];
        }
        printf("%f\n", ans);
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值