Codeforces Gym 101174 D. Dinner Bet (期望 + DP)

14 篇文章 0 订阅
4 篇文章 0 订阅

Problem

一黑盒中有 N 个球,每球标号分别为 1~N 。游戏根据下列步骤进行:

  • 初始时刻,两个人从 1~N 中选择 C 个不同的数字并记录。
  • 在每轮游戏中,完全随机地从黑盒中同时取出 D 个球,同时两人标记自己的 C 个数字中恰好是抽出的某一球编号的数字。后将 D 个球放回。
  • 当至少有一人的 C 个数全部被标记,游戏结束。

问游戏结束的期望轮次。

限制条件

1 ≤ N ≤ 50

1 ≤ D ≤ min(10,N)

1 ≤ C ≤ min(10,N)

解题思路

由于此题要求的期望结果的精度较低,绝对误差 <103 <script type="math/tex" id="MathJax-Element-3"><10^{-3}</script> 。故可以模拟前 1000 次的结果来获得粗略的概率。

dp[TIMES][j][k][t] ,表示第 TIMES 轮游戏,第一人 A 独有的数字还有 j 个未被标记,第二人 B 独有的数字还有 k 个未被标记,AB 共有的数字还有 t 个未被标记的概率。

状态转移为 若前一轮的合法概率 dp[TIMES-1][jj][kk][tt]

dp[TIMES][j][k][t]+=dp[TIMES1][jj][kk][tt]CjjjjjCkkkkkCtttttCDj+jjk+kkt+ttNjjkkttCDN

统计所有 dp[TIMES][j][0][0]dp[TIMES][0][k][0]dp[TIMES][0][0][0] 的概率和轮次的乘积和即可。

代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100;
const int TIMES = 1000;
int N, D, C, MC, MT, MR;
int card[2][11], isComm[51];
double dp[TIMES][11][11][11];
double di[TIMES][11][11][11];
long long c[MAXN][MAXN];
void init(){
    c[0][0]=1;
    for(int i=1;i<=50;++i){
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;++j)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    }
}
void solve()
{
    for(int i=1;i<TIMES;i++)
    for(int j=MC;j>=0;j--)
    for(int k=MC;k>=0;k--)
    for(int t=MT;t>=0;t--)
    {
        if(N - (j+k+t) < D) continue;
        for(int jj=MC;jj>=j;jj--)
        for(int kk=MC;kk>=k;kk--)
        for(int tt=MT;tt>=t;tt--)
        {
            if(jj+kk+tt-j-k-t > D || jj+tt == 0 || kk+tt == 0)  continue;
            dp[i][j][k][t] += dp[i-1][jj][kk][tt] / c[N][D] * c[jj][jj-j] * c[kk][kk-k] * c[tt][tt-t] * 
                c[N-jj-kk-tt][D-(jj-j+kk-k+tt-t)];
        }
    }
}
int main()
{
    init();
    scanf("%d %d %d", &N, &D, &C);
    for(int idx=0;idx<2;idx++)
        for(int i=1;i<=C;i++)
        {
            scanf("%d", &card[idx][i]);
            isComm[card[idx][i]]++;
            if(isComm[card[idx][i]] == 2)
                MT++;
        }
    MC = C - MT;
    MR = N - MC*2 - MT;
    dp[0][MC][MC][MT] = 1.0;

    solve();

    double ans = 0;
    for(int i=1;i<TIMES;i++)
    {
        for(int j=MC;j;j--)
            ans += i*dp[i][j][0][0],
            ans += i*dp[i][0][j][0];
        ans += i * dp[i][0][0][0];
    }   

    printf("%.10lf\n", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值