Problem
一黑盒中有 N 个球,每球标号分别为 1~N 。游戏根据下列步骤进行:
- 初始时刻,两个人从 1~N 中选择 C 个不同的数字并记录。
- 在每轮游戏中,完全随机地从黑盒中同时取出 D 个球,同时两人标记自己的 C 个数字中恰好是抽出的某一球编号的数字。后将 D 个球放回。
- 当至少有一人的 C 个数全部被标记,游戏结束。
问游戏结束的期望轮次。
限制条件
1 ≤ N ≤ 50
1 ≤ D ≤ min(10,N)
1 ≤ C ≤ min(10,N)
解题思路
由于此题要求的期望结果的精度较低,绝对误差 <10−3 <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[TIMES−1][jj][kk][tt]∗Cj−jjjj⋅Ck−kkkk⋅Ct−tttt⋅CD−j+jj−k+kk−t+ttN−jj−kk−ttCDN
统计所有 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);
}