排列组合的数学逻辑
前言
本系列上一篇博客 讲的是如何用代码实现排列组合, 本篇博客讲的是排列组合的数学逻辑, 类似于高中数学里面排列组合的习题思路.
排列
圆排列
如果 n 个不同的数排成一列, 不同的方案数是 n ! n! n!
如果 n 个不同的数排成首尾相接的一个环, 比如 n = 3, 分别为 1, 2, 3
那么
{ 1 , 2 , 3 } ≡ { 2 , 3 , 1 } ≡ { 3 , 1 , 2 } \{1, 2, 3\} \equiv \{2, 3, 1\} \equiv \{3, 1, 2\} {
1,2,3}≡{
2,3,1}≡{
3,1,2}
也就是说, 每一种排列都有另外的 n - 1 种排列与之等价, 因此不同的方案数公有 n ! / n n! / n n!/n 个
易知, 如果不是全排列, 那么不同的方案数有 P ( n , r ) r \frac{P(n, r)}{r} rP(n,r) 个
项链排列
项链排列是在圆排列的平移等价性上多了一个翻转等价性, 还拿 1, 2, 3 来说,
{ 1 , 2 , 3 } ≡ { 1 , 3 , 2 } \{1, 2, 3\} \equiv \{1, 3, 2\} {
1,2,3}≡{
1,3,2} (脑补一下三角形水平翻转)
因此, 项链排列的个数有 P ( n , r ) 2 r \frac{P(n, r)}{2r} 2rP(n,r) 个.
组合
组合比较难. 以下通过放球模型 (即 n 个小球放到 m 个盒子里) 来解释, 根据小球是否相同, 盒子是否相同, 是否允许有空盒子, 一共有八种情况
n 个相同的小球放到 m 个相同的盒子里
允许空盒子
用动态规划做.
记 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示将 j j j 个小球放入至多 i i i 个盒子的方法数 (因为允许空盒子, 所以 i 表示的是最多能放入 i 个盒子).
然后规定 d p [ 0 ] [ j ] = d p [ 1 ] [ j ] = d p [ i ] [ 1 ] = 1 dp[0][j] = dp[1][j] = dp[i][1] = 1 dp[0][j]=dp[1][j]=dp[i][1]=1, 即如果没有小球, 或者只有 1 个小球, 或者只有一个盒子, 那么方案数都是 1
然后有一个显然的关系: 如果 i > j, 那么 d p [ i ] [ j ] = d p [ j ] [ j ] dp[i][j] = dp[j][j] dp[i][j]=dp[j][j], 意思是如果盒子的数量比小球多, 那么不论再怎么分, 最多只有 j (小球的个数) 个盒子里面有球, 也就是 d p [ j ] [ j ] dp[j][j] dp[j][j] 表示的值.
最后是转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − i ] dp[i][j] = dp[i-1][j] + dp[i][j-i] dp[i][j]=dp[i−1][j]+dp[i][j−i]
意思是, 将 j 个小球放入至多 i 个盒子的方案数, 等于将 j 个小球放入至多 i-1 个盒子的方案数( d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j]) + 将 j 个小球放入正好 i 个盒子的方案数( d p [ i ] [ j − i ] dp[i][j-i] dp[i][j−i]).
这里解释一下为什么正好 i 个盒子的方案数是 d p [ i ] [ j − i ] dp[i][j-i] dp[i][j−i], 假设有 5 个小球和 3 个盒子, 那么如果想让小球正好放入 3 个盒子里, 那么每个盒子至少有一个球. 所以如果先从小球中拿出 3 个来给每个盒子放入一个球, 那么问题就变成了将剩下的2个小球随意 (允许空盒子) 放入3个盒子里, 即 d p [ i ] [ j − i ] dp[i][j-i] dp[i][j−