Coins UVALive - 8468

题目链接

借鉴的思路大神

       简述题意,一开始有 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值