题意:
一个愉快简单的游戏:有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;
}