把n个巧克力放进m个盒子中,求至少有一个盒子为空的概率
直接求解情况比较多,可以考虑先求每个盒子都不空的概率
要求每个盒子至少有一个,可以先在每个盒子中放一个巧克力,在任意去放剩下的n-m个巧克力
由于巧克力是一样的,盒子也是一样的,所以可以考虑为将n-m个相同的巧克力分组,组数最多为m组
每一种分组对应一种放法,只要知道有多少不同的分组就行了
但这种做法我没有办法想出一个递推公式或是通项公式,网上也没找到,好像需要分类讨论
这里我们用动态规划来做
我们用dp[i][j]表示i个巧克力放进j个盒子使得每个盒子都不为空的概率
注意这里盒子可能并没有放满,我们考虑的是在这m个盒子中,我们用i个巧克力放满j个盒子的概率,和原问题不等价
当我们放第i个巧克力的时候,对于已经放好的巧克力有两种情况,1 i-1个巧克力已经使得j个盒子每个盒子都有一个巧克力,所以这时我们只需要往j个盒子中任意放一个巧克力即可,结果为dp[i-1][j]/j
2 i-1个巧克力使得j-1个盒子都有一个巧克力,所以我们需要在剩余的m-j-1个盒子中任意放一个巧克力即可,
结果为dp[i-1][j-1]/(m-j-1)
综上 dp[i][j]=dp[i-1][j]/j+dp[i-1][j-1]/(m-j-1)
感想 计数问题有时会与dp问题相关联,但万变不离其宗,都需要找到一个递推公式
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iomanip>
#include<string>
#include<stack>
#include<algorithm>
#pragma warning(disable:4996)
using namespace std;
int main()
{
double dp[105][105], f[106];
int i, j, k, n, m;
k = 0;
while (cin >> n) {
if (n == -1) break;
cin >> m;
memset(dp, 0, sizeof(dp));
for (i = 1; i <= m; i++) f[i] = (double)i / m;
dp[1][1] = 1;
for (i = 2; i <= n; i++)
for (j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j] * f[j] + dp[i - 1][j - 1] * f[m - j + 1];
}
printf("Case %d: %.7f\n", ++k, 1 - dp[n][m]);
}
}