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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章