关于几类放球问题的总结

总览:
球入盒问题

  1. 无->无
     (1) 放苹果问题;
     (2) 不降序列计数问题;
     (3) 要求无一空盒的拓展;
  2. 无->有
     (1) 不定方程非负整数解;
     (2) 可重组合;
     (3) 要求无一空盒的拓展;
  3. 有->无
     (1) 集合无序划分;
     (2) 第二类斯特林数(要求无一空盒);
     (3) bell数;
     (4) 斯特林数求和;
  4. 有->有
     (1) 集合有序划分、可重排列; \(S(n,m)=m^n\)
     (2) 结论(1)的两种证明方法(代数证明,生成函数证明);
     (3) 盒子可以为空:第二类斯特林数*排列系数再求和;
     (4) 要求无一空盒:无标号盒子方案数*m! = 有标号盒子方案数;
     (5) 盒子中的球有顺序的拓展;

放球问题,指的是这样一类问题:给你n个球和m个盒子,球和盒子可以有标号也可以没有标号,问将这n个球放入这m个盒子有多少种不同方案?默认情况下允许存在空盒,但有的类型会要求无一空盒,下面我们会讨论到。

我们把把n个有/无标号的球放入m个有/无标号的盒子中的问题记作 n有/无->m有/无 ,例如,n有->m无 表示将n个有标号的球放入m个无标号的盒子的问题,其方案数记为F(n,m),在此基础上,要求无一空盒的方案数记为F'(n,m)。

第一类:n无->m无

这是经典的放苹果问题,有递推式F(n,m)=F(n,m-1)+F(n-m,m),相当于讨论是否存在空的盘子,如果有则方案数为F(n,m-1),没有则为F(n-m,m)。
这个问题也等价于求有多少个不降的非负整数序列\(\{a_m\}\),使得\(\sum_{i=1}^{m}a_i=n\),可以设一个略微复杂的dp方程:设f[i][j][k]为序列前i个数的总和为j,且第i个数为k的方案数,有转移方程:\[f[i][j][k]=\sum_{l=0}^{k} f[i-1][j-k][l]\]
但是发现这样dp实在太傻了,因为如果我们像上面放苹果那样设f[i][j]为长度为i且总和为j的不降序列方案数的话,就只需要讨论序列第一个数是是不是0就可以了,如果是0,就可以从f[i-1][j]转移过来,如果不是0,就可以考虑将序列每一个数减去一个1,就可以从放f[i][j-i]转移过来了。于是我们得到一个简单得多的式子:
\[f[i][j]=f[i-1][j]+f[i][j-i]\]
和上面的放苹果问题一模一样。

若在此基础上要求无一空盒,因为每个盒子中的球数都>0,所以可以考虑将每一个盒子中的球数减1,就变成了允许空盒的情况,设不允许空盒的方案数为F'(n,m),则有F'(n,m)=F(n-m,m)。

第二类:n无->m有

即求满足\(\sum_{i=1}^{m}a_i=n\)的非负整数序列\(\{a_m\}\)的数量,是经典的不定方程非负整数解问题,用可重组合数直接求出方案数为\(F(n,m)=\overline{C}_{n+1}^{m-1}=C_{n+m-1}^{m-1}\)

从另一个角度考虑,F(n,m)也可以看作是从m个数中不计顺序的取n个数,允许重复的方案数,即从m个数中取n个数的可重组合数\(\overline{C}_m^n\),又因为将n个数放入m个盒子中可以看作是将这n数分成m分,又可以转化为将m-1个板子插到这n个数之间,也即将m-1个板子和n个数交叉混合,问题就变成了给定(n+m-1)个位置,从中选出n个位置放数字,其他位置放板子的方案数,方案数就等于\(C_{n+m-1}^{n}\),这就证明了\(\overline{C}_m^n=C_{n+m-1}^{n}\),同时也证明了\(F(n,m)=C_{n+m-1}^{n}\)

至于要求无一空盒的方案数,可以用类似于第一类问题的方法处理。

第三类:n有->m无

这相当于一个n元集合的m无序划分数(允许划分出来的集合为空)。
一看到这个就想到了第二类斯特林数,第二类斯特林数S(n,m)指的是将n个有标号的球放入m个无标号的盒子中,要求无一空盒的方案数,接下来的讨论中会经常用到它,要详细了解第二类斯特林数的话可以参考gzy学长的总结,这里只简单写一个它的递推公式:
\[S(n,m)=S(n-1,m-1)+m*S(n-1,m)\]
其中\(S(0,0)=1,S(0,i)=0(i>0),S(i,0)=0(i>0)\),这个式子的意思是考虑第一个球是否单独占一个盒子,若单独一个盒子,那么剩下n-1个球就可以放入剩下m-1个盒子里,方案数S(n-1,m-1),若和其他球在同一个盒子里,就可以先让剩下n-1个球放入m个盒子里,再把第一个球放入m个盒子中的任意一个,方案数m*S(n-1,m),两种情况加起来就得到了这个递推式。

让我们再考虑另一种特殊情况,即没有限定多少个盒子,也就是这n个球可以放入任意多个盒子中,可以发现这种情况等价于有n个盒子(n个球最多占用n个盒子),且允许盒子为空,这时的方案数就是F(n,n),且它等于Bell(n),其中Bell数为集合划分数,它和第二类斯特林数之间有公式\(Bell(n)=\sum_{i=0}^n S(n,i)\),这很好理解,就是枚举非空盒的数量,然后累加即可。

于是我们可以得出F(n,m)的计算式了:
\[F(n,m)=\sum_{i=0}^m S(n,i)\]
和上面一样,枚举非空盒的数量即可。

要求无一空盒的方案数就是S(n,m)

第四类:n有->m有

这相当于一个n元集合的m有序划分数,容易发现这n个球之间是相互独立的,且每个球可以放入m个盒子中的任意一个,所以方案数\(F(n,m)=m^n\)

这东西用组合意义很好证明,但是我在用代数证明时却发生了一些很有趣的事。
首先我们用代数式子来表示这个东西,我们可以先枚举一个序列\(\{a_m\}\)表示每个盒子中球的数量,再将n个球按照这个数量填入盒子中,就像这样:
\[F(n,m)=\sum_{a_1+a_2...+a_m=n}C_{n}^{a_1}C_{n-a_1}^{a_2}...C_{n-a_1-a_2...-a_{m-1}}^{a_m}\]
化一下式子得到
\[\sum_{a_1+a_2...+a_m=n}\frac{n!}{a_1!a_2!...a_m!}\]
也即
\[n!\sum_{a_1=0}^{n}\frac{1}{a_1!}\sum_{a_2=0}^{n-a_1}\frac{1}{a_2!}...\sum_{a_{m-1}=0}^{n-a_1-a_2...-a_{m-2}}\frac{1}{a_{m-1}!}\frac{1}{(n-a_1-a_2...-a_{m-1})!}\]

\[G(n,m)=\sum_{a_1=0}^{n}\frac{1}{a_1!}\sum_{a_2=0}^{n-a_1}\frac{1}{a_2!}...\sum_{a_{m-1}=0}^{n-a_1-a_2...-a_{m-2}}\frac{1}{a_{m-1}!}\frac{1}{(n-a_1-a_2...-a_{m-1})!}\]
可以得出递推式
\[G(n,m)=\sum_{i=0}^{n}\frac{1}{i!}G(n-i,m-1)\]
其中\(G(n,1)=\frac{1}{n!}\)
接下来让我们归纳证明\(G(n,m)=\frac{m^n}{n!}\)
当m=1时,\(G(n,1)=\frac{1}{n!}=\frac{1^n}{n!}\),显然成立。
假设m < k时命题成立,试证m=k时,\(\forall n\),命题也成立:
\[G(n,m)=\sum_{i=0}^{n}\frac{1}{i!}G(n-i,m-1)\]
\[=\sum_{i=0}^{n}\frac{1}{i!}\frac{(m-1)^{n-i}}{(n-i)!}\]
\[=\frac{1}{n!}\sum_{i=0}^{n}C_{n}^{i}1^i(m-1)^{n-i}\]
\[=\frac{1}{n!}m^n=\frac{m^n}{n!}\]
命题得证。
所以\(G(n,m)=\frac{m^n}{n!}\),那么\(F(n,m)=n!*G(n,m)=m^n\)

一旁的神仙Itst看我证的那么辛苦,当即给了我一个简单的生成函数证法:
考虑\(G(n,m)=\sum_{a_1+a_2...+a_m=n}\frac{1}{a_1!a_2!...a_m!}\)的生成函数,容易发现\(G(n,m)\)就等于\((1+\frac{x}{1!}+\frac{x^2}{2!}+\frac{x^3}{3!}+...)^m\)的第\({x^n}\)项系数,而\((1+\frac{x}{1!}+\frac{x^2}{2!}+...)=e^x\),所以\((1+\frac{x}{1!}+\frac{x^2}{2!}+...)^m=e^{mx}\),展开后就是\(1+\frac{m}{1!}x+\frac{m^2}{2!}x^2+\frac{m^3}{3!}x^3...\),第\(x^n\)项系数就是\(\frac{m^n}{n!}\),所以\(F(n,m)=m^n\)

这就证完了。。。(被虐爆了

另一方面,我们同样可以用第二类斯特林数计算F(n,m),同样枚举非空盒的数量,然后对于有i个非空盒的情况,要乘以一个排列系数\(P_m^i\),即要将i个划分出来的无序集按顺序填入m个盒子中,所以可以得到
\[F(n,m)=\sum_{i=0}^{m}P_m^i*S(n,i)\]
\(F(n,m)=m^n\)
所以\[\sum_{i=0}^{m}P_m^i*S(n,i)=m^n\]
这东西可以用来求第二类斯特林数通项公式,这里暂不深究。

如果要保证无一空盒怎么办?直接\(F'(n,m)=m!*S(n,m)\)即可(就是将所有无序划分做一个全排列就得到了所有有序划分)

还有一个这类问题的变种:将n个有标号的球放入m个有标号的盒子里,允许有空盒,且盒子里的球是有顺序的,即将n个球划分到m个盒子里后还可以将每个盒子里的球按任意顺序排列,例如2个球2个盒子就有\(\{\}\{1,2\},\{\}\{2,1\},\{1,2\}\{\},\{2,1\}\{\},\{1\}\{2\},\{2\}\{1\}\)这六种方案,求这样的方案数\(F_1(n,m)\)

根据组合意义,我们可以一个一个球依次考虑,第一个球有m个位置可以放,第二个有(m+1)个位置(除了可以放在m个盒子的末尾,还可以放在第一个球的前面),第三个可以放在第一、第二个的前面,有(m+2)个位置可以放......以此类推,第i个球有(m+i-1)个位置可以放,于是有:
\[F_1(n,m)=m*(m+1)...*(m+n-1)=m^{\overline{n}}\]

这个式子也可以用上面证\(F(n,m)=m^n\)的代数方法类似地证明

转载于:https://www.cnblogs.com/lishuyu2003/p/11381444.html

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
函数模板和类模板是C++中非常重要的特性,它们可以大大提高代码的复用性和灵活性。在进行函数模板和类模板的实验过程中,我总结出以下几点经验: 1. 模板的定义和使用方式: 函数模板和类模板的定义方式类似,都是使用template关键字和尖括号来定义模板参数。在使用时,需要在函数名或类名后面加上尖括号,并在尖括号中指定具体的模板参数。例如: ``` template<typename T> void swap(T& a, T& b) { T temp = a; a = b; b = temp; } template<typename T> class Stack { public: void push(const T& value); T pop(); private: std::vector<T> elements; }; ``` 2. 模板参数的类型推导: 当我们调用一个模板函数或实例化一个模板类时,可以通过函数参数或构造函数参数来推导模板参数的类型。例如: ``` int a = 1, b = 2; swap(a, b); // 编译器会自动推导出T为int类型 Stack<int> intStack; intStack.push(1); // 编译器会自动推导出Stack的模板参数T为int类型 ``` 3. 模板特化: 有时候我们需要为某些特定类型的参数定义特殊的处理方式,这时可以使用模板特化。例如: ``` // 为字符串类型定义特化版本 template<> void swap<std::string>(std::string& a, std::string& b) { std::string temp = a; a = b; b = temp; } // 为bool类型定义特化版本 template<> class Stack<bool> { public: void push(bool value); bool pop(); private: std::vector<bool> elements; }; ``` 4. 模板的局限性: 模板的使用也有一些局限性,例如模板参数必须是可复制的类型,无法处理运行时动态类型,模板的实例化只能在编译期完成等。在使用模板时需要注意这些局限性。 通过这些实验,我更加深入地了解了函数模板和类模板的使用方式和特性,也学会了如何灵活地使用模板来提高代码的复用性和灵活性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值