组合计数
计数基本原理
∣A∣=a∈A∑1
加法定理
|A U B| = |A| + |B|
乘法定理
|A * B| = |A| * |B|
举例
-
n-bit二进制串一共有多少个: 2^n
(乘法原理)
-
如果把0
看作(
,把1
看作)
,配对的括号序列有多少个?
0011
->(())
101010
->)()()(
对于长度为6的序列:
序列=⎩⎨⎧()(()),()()(),(())(),((())),(()()),k=2k=2k=4k=6k=6
第一个位置只能是(
,考虑第一个(
匹配的右括号位置在偶数位(2,4,6…),分析所有的组合序列
如果位置是2,那么括号里边只有空集,外边的长度是4,有1*2 = 2
种情况
如果位置是4,那么里边长度2,外边长度2,都只有1*1 = 1
种情况
如果位置是6,那么里边长度4,外边是空集,那么有2*1 = 2
种情况
拓展到长度为n,则:
f(n)=k∑f(k)⋅f(n−k)
组合数
概念
组合数的定义是从n
个不同元素中取出m(m≤n)
个元素的所有组合的个数,叫做从n
个不同元素中取出m
个元素的组合数
公式
从n
个数里找m
个数排列(比如找第一个数有n
个)得到Anm,再除以Amm去除这m
个数的排列,得到组合数
Cnm=AmmAnm=m!⋅(n−m)!n!
互补性:Cnm=Cnn−m
n-1
个数中找m
个数,在n-1
个数中加1
个数,这个数可能在m
个数中,也可能不在
Cnm=Cn−1m−1+Cn−1m
思考
事实上,从n个数中找m个数
相当于把m个数插入到(n-m)个数中
举证
求 1
到 n
所有排列的数量
利用乘法原理可以直接得出答案:n!
此时继续考虑把n
个数分成m
和n-m
两组
例如,1234567
可以分成123
和4567
123
有3!
个排列
4567
有4!
个排列
对于123
和4567
的所有排列分别取一种情况,比如就取1->2->3
和4->5->6->7
那么在保持顺序不变的情况下,设把123
添加到4567
当中的所有排列情况一共有x
个
那么7!=3!⋅4!⋅x
推广到n
和m
:
n!=m!⋅(n−m)!⋅x
则 x=m!⋅(n−m)!n!
错位排列
求1到n的排列中,每个数字不在原位的排列数量
f(n)=(n−1)⋅(f(n−1)+f(n−2))
f(1) = 0, f(2) = 1
举证
对于12345678
这些数字
第一个数字可以是2345678
,设第一个数字取2
,那么
1−2−3−4−5−6−7−8↑2−1−3−4−5−6−7−8
考虑第二个位置,如果取1
,那么剩下的问题就是对345678
求错位排列
如果第二个位置不取1
,那么相当于第二个位置也是要求错位,那么剩下的问题就是对2345678
求错位排列
所以f(8)=7⋅(f(7)+f(6))
容斥原理
∣A∩B∣=∣A∣+∣B∣−∣A∪B∣
许多问题都有"求交简单,求并难"的性质
更多集合的容斥原理
“减多了,要加回去”
∣A∩B∩C∣=∣A∣+∣B∣+∣C∣−∣A∪B∣−∣A∪C∣−∣B∪C∣+∣A∪B∪CD
可以拓展到n个集合的情况+-+-+...
给你一个整数数组 coins
表示不同面额的硬币,另给你一个整数 k
。
你有无限量的每种面额的硬币。但是,你 不能 组合使用不同面额的硬币。
返回使用这些硬币能制造的 第 k^th
小 金额。
示例 1:
输入: coins = [3,6,9], k = 3
输出: 9
**解释:**给定的硬币可以制造以下金额:
3元硬币产生3的倍数:3, 6, 9, 12, 15等。
6元硬币产生6的倍数:6, 12, 18, 24等。
9元硬币产生9的倍数:9, 18, 27, 36等。
所有硬币合起来可以产生:3, 6, 9, 12, 15等。
示例 2:
**输入:**coins = [5,2], k = 7
**输出:**12
**解释:**给定的硬币可以制造以下金额:
5元硬币产生5的倍数:5, 10, 15, 20等。
2元硬币产生2的倍数:2, 4, 6, 8, 10, 12等。
所有硬币合起来可以产生:2, 4, 5, 6, 8, 10, 12, 14, 15等。
提示:
1 <= coins.length <= 15
1 <= coins[i] <= 25
1 <= k <= 2 * 10^9
coins
包含两两不同的整数。
二分+容斥原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class Solution { private long gcd(long a, long b) { if (b == 0) return a; return gcd(b, a%b); } private long count(long k, List<long[]> lcmAndCnt) { long res = 0; for (long t[] : lcmAndCnt) { if (t[1] % 2 == 1) { res += k / t[0]; } else { res -= k / t[0]; } } return res; } public long findKthSmallest(int[] coins, int k) { int n = coins.length; long l = 1, r = 25L * k; List<long[]> lcmAndCnt = new ArrayList<>(); for (int i = 1; i < 1<<n; i++) { long lcm = 1, cnt = 0; for (int j = 0; j < n; j++) { if ((i >> j & 1) == 1) { lcm = lcm / gcd(lcm, coins[j]) * coins[j]; cnt++; } } lcmAndCnt.add(new long[]{lcm, cnt}); } while (l < r) { long m = l + r >>> 1; if (count(m, lcmAndCnt) < k) l = m + 1; else r = m; } return l; } }
|
求与n互质的正数个数
例如n = 5 * 7 * 13
与n不互质(因数)的个数 = n/5 + n/7 + n/13 - n/(5*7) - n/(5*13) - n/(7*13) + n/(5*7*13)
res = n - 不互质的个数