題意:
一個愉快簡單的遊戲:有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;
}