十六种盒子放球问题的集合

大概是组合计数问题的基础,因此稍微写一下。

或者说,尝试复习,发现自己都不会了,所以应该写一下。

约定

这一类问题都可以在问题确定是,用两个参数 n,r 来描述。其中 n 表示球数, r 表示盒数。

为了方便描述,以下用一串二进制码表示问题的状态。例如 0101

  1. 第一位表示球是否相同。 0 表示相同, 1 表示不同;
  2. 第二位表示盒是否相同。 0 表示相同, 1 表示不同;
  3. 第三位表示球可否不放。 0 表示不可, 1 表示可以;
  4. 第四位表示盒可否为空。 0 表示不可, 1 表示可以;

因此状态 0101 就表示:球相同,盒不同,球不可不放,盒可以为空,此时的方案数。

另外,由于有些问题是笔者自己 yy 的,因此正确性存疑,该类问题用 * 标注。如果发现该部分有问题,请及时留言

11--

球不同、盒不同问题求解的基本途径是乘法原理

1101

对于每一个球,它都有 r 种选择。由于每个球不同,因此它们的选择相互独立,可以直接乘起来。

因此可以得到总方案数为 rn ,求解的时间复杂度是 O(log2n)

1100

此时盒子不可以为空,但是直接乘法原理我们会得到盒子为空的方案。

注意乘法原理求到的是 " 至少有 0 个盒子为空 " 的方案数。因此不难想到用容斥原理计算。枚举一下空盒子的数量,就可以方案数是:

i=0r(1)i(ri)(ri)n

求解的时间复杂度是 O(rlog2n)

r 比较小的情况下,可以 O(r) 预处理自然数幂,然后求式子,时间复杂度是 O(r)

还可以发现,如果用指数型生成函数描述这个式子,我们可以在 O(rlog2r) 的时间批量求出不同的 r 的解。

你会在之后再见到它的。

1110

这个时候,每个球就多了一个 " 不放 " 的选择。因此每个球有 r+1 种选择。总方案数是 (r+1)n

1111

继续容斥:

i=0r(1)i(ri)(r+1i)n

01--

球相同、盒不同问题求解的基本原理是隔板法

0101

想象 n 个球排成一列。此时由于盒子是有区别的,因此我们可以将球分成 r,然后第一组放进第一个盒子里,第二组放进第二盒......第 r 组放进第 r 盒。因此我们就考虑计算将 n 个球分成 r 组的方案数。

显然这是隔板法的问题。 n 个球有 n1 个缝,从 n1 个缝中选出 r1 个来插板的方案数是 (n1r1)

0100

盒子可以为空,因此直接放板子不太对头。经典的思想是:

我们给每个盒子先放一个 " 假球 " ,这样总共有 n+r 个球,并且每个盒子至少有一个球(包括 " 假球 " )。现在再进行隔板法就没有问题了,方案就是 (n+r1r1)

0110*

由于球本身没有区别,因此有球没放就相当于球的数量变少了。于是就可以直接枚举球的数量:

i=1n(i1r1)=i=1n(i1)r1_(r1)!=1(r1)!i=0n1ir1_=1(r1)!0nxr1_δx=1(r1)!nr_r=(nr)

这个组合含义也比较显然。我们同样可以假想一个盒子,用来装 " 没放 " 的球。由于最后这个盒子可以为空,因此总共有 n 个缝, r 个板。

注意,盒子不能为空,因此这里不考虑不放球的情况。

0111*

同理易得:

i=0n(i+r1r1)=i=0n(i+r1)r1_(r1)!=1(r1)!i=0n(i+r1)r1_=1(r1)!r1n+rxr1_δx=1(r1)!(n+r)r_(r1)r_r=(n+rr)

组合意义请自行思考。

注意,盒子可以为空,因此这里需要考虑球都不放的情况。

10--

球不同、盒相同问题求解的基本途径是第二类斯特林数

1000

此时直接推导难度比较大,因此我们考虑使用 DP 。

f(i,j) :有 i 个球, j 个盒子,且球不同,盒相同时,球必须放,盒不能空的放球的方案数。

考虑转移:

f(i,j)={1i=0,j=00i<j0i>0,j=0f(i1,j1)+jf(i1,j)otherwise

最后一个转移是在讨论,在放最后一个球时,是否要新拿盒子。如果新拿一个,就是 f(i1,j1) 。如果不新拿,就从前 j 个盒子中挑一个放进去。

如果你有所了解,你就会发现,这是第二类斯特林数的递归式。这里我们记 {nr}=f(n,r)

因此这个问题的答案是 {nr}

另一种推导方式 是,考虑容斥。我们先给盒子标号,然后枚举空盒子的数量,最后把标号除掉:

{nr}=1r!k=0r(1)k(rk)(rk)n

可以发现,后面的容斥式实际就是 1100 的解。因此 1100 的解也可以表述为 r!{nr}

另外,根据这个容斥式也可以使用 NTT 在 O(nlog2n) 的时间内求出 {n0},{n1},,{nk}

补充内容:

斯特林数原本用于描述阶乘幂和幂之间的系数关系。第一类斯特林数 [nk] 用于描述 xn_ 展开中 xk 的系数,第二类 {nk} 用于描述 xn 展开中 xk_ 的系数。以此,幂和阶乘幂就可以很方便地进行转换。
因此可以得到关系式:

xn_=k=0n[nk]xkxn=k=0n{nk}xk_

当然,两种数都有其对应的组合含义。

1001

不难想到,可以直接枚举有球的盒子的数量:

i=0r{ni}

补充说明:

r=n 的时候,问题就变成了,对于大小为 n 的集合 S ,将它划分成任意多个非空子集的方案数是多少?
专门有一个数列 {bn} 来描述它。这类数就叫 " 贝尔数 " 。
简单推导(考虑第 n 个元素所在集合大小)可以得出贝尔数的转移:

bn={1n=0k=1n(n1k1)bnkotherwisebn=k=0n{nk}

可以定义 B(x) 为贝尔数的指数型生成函数,那么就有 B(x)=eex1 。具体推导可以参考 洛谷日报 等资料。

1010*

同样可以枚举球的数量:

k=0n(nk){kr}

你发现这个式子仍然可以使用 NTT 批量计算。甚至此情况的答案可以直接写成生成函数形式。

1011*

同样可以枚举球和空盒子的数量:

j=0n(nj)k=0r{jk}

00--

球相同、盒相同问题求解的基本原理是动态规划

0001

不难发现,此时我们可以直接用每个盒子的球数组成的序列来描述一种方案。

那么我们只枚举单调不减的序列就好了,也就是说,原问题的等价于求自然数序列的数量:

{i=1rai=n1<ir,ai1ai

这里有一个很常见的转化:枚举单调不降序列,就相当于枚举全是 1 的后缀的和
具体来说,我们可以定义

si={0 0  0i10 1 1  1ni+11}

那么一个单调不降的序列就必然可以拆分成多个 s 的对应位的和。比如 n=4 时, {1 2 3 3}=s1+s2+s3

于是不难想到一个完全背包

g(i,j):在考虑完 s1sj 后,所有后缀的和为 i 的方案数。

可以得到转移为:

g(i,j)={1i=00i>0,j=0g(ij,j)+g(i1,j)othewise

此时的答案就是 g(n,r)

0000

此时盒子不能为空,我们可以直接给每个盒子分配一个球。当 n<r 时,答案是 0 。当 nr 时,答案就是 g(nr,r)

0011*

球没有区别,因此可以直接枚举数量:

k=0ng(k,r)

0010*

基本同上:

k=rng(kr,r)

总结

盒子放球问题虽然模型简单,但是你可以发现,在 16 种问题中,我们用了 4 种主要的思路:乘法原理、隔板法、斯特林数、动态规划,以及一些常见的技巧,比如容斥。每种主要思路下, 4 个子问题的方法不尽相同。不同主要思路下,处理子问题的限制的方法各自有些相似。

所以说,深入学习盒子放球,对于学习计数是比较有意义的。它的确可以帮助你熟悉一些基础的计数方法。

posted @   crashed  阅读(1525)  评论(2编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示