借鉴的思路大神
简述题意,一开始有 n 枚硬币反面朝上,有 m 次机会,每次机会必须选取 k 枚硬币上抛,问在最优策略下,最后能有多少枚硬币正面朝上。
用dp[i][j]表示第i次上抛结束后j枚硬币朝上的概率;
首先最优策略是第 i 次上抛结束后有 j 枚硬币一定正面朝上,则第i+1次选取k枚硬币时
1.如果 j + k <= n,也就是此时反面朝上的硬币数量 >= k,那么我们此时肯定要从反面朝上的硬币中选,dp[i+1][j+t]就表示第i+1次结束以后,j+t枚硬币正面朝上的概率,dp[i+1][j+t] += dp[i][j] * C(k,t) / (2^k);
2.如果j + k > n,也就是反面朝上的硬币数量 <k,令 tt = j + k - n,也就是说要从已经正面朝上的硬币中选取tt枚硬币上抛,从反面朝上的硬币中选取k - tt枚硬币上抛,则i+1次上抛硬币结束后一定正面朝上的硬币数量为 j - tt,因此i+1次后,dp[i+1][j - tt + t] = dp[i][j] * C(k,t) / (2^k);
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <cmath>
#include <stack>
#include <map>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mod 1000000007
using namespace std;
const int maxn = 110;
double c[maxn][maxn],dp[maxn][maxn];
void init()
{
for(int i=0; i<=100; i++)
{
c[i][0] = 1;
for(int j=1; j<=i; j++)
{
c[i][j] = c[i][j-1] * (i - j + 1) / j;
}
}
double t = 1;
for(int i = 0; i<=100; i++)
{
if(i) t *= 2;
for(int j=0; j<=i; j++)
{
c[i][j] /= t;
}
}
}
int main()
{
int t;
init();
scanf("%d",&t);
while(t--)
{
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
memset(dp,0,sizeof(dp));
dp[0][0] = 1.0;
for(int i=0; i<=m; i++)
{
for(int j=0; j<=n; j++)
{
if(j + k <= n)
{
for(int t = 0; t <= k; t++)
{
dp[i+1][j+t] += dp[i][j] * c[k][t];
}
}
else
{
int tt = j + k - n;
for(int t = 0; t <= k; t++)
{
dp[i+1][j - tt + t] += dp[i][j] * c[k][t];
}
}
}
}
double ans = 0;
for(int i=1; i<=n; i++)
ans += i * dp[m][i];
printf("%.3f\n",ans);
}
return 0;
}