生成排列和子集
这个只是单纯的总结,之后肯定还会搞的(如果没有退役的话)。
生成排列
不妨以生成 $1 \sim n$ 的排列进行举例子,我们对于每一个数字的上面先定一个箭头,表示其可以向那边走。
一个数字移动的条件是其箭头指向的相邻的数组比其要小。
例如 $\overset{\leftarrow}{1}\overset{\leftarrow}{2}$ 这个时候 $2$ 就是可以向左走一个位置的,也就是意味着下一个排列是 $\overset{\leftarrow}{2}\overset{\leftarrow}{1}$。
我们构造一个排列是从 $\overset{\leftarrow}{1}\overset{\leftarrow}{2} \dots \overset{\leftarrow}{n}$ 开始的。
- 每次找到最大的可以移动的数。
- 交换其和其箭头指向的数字。
- 交换所有 $i, i > m$ 的箭头的方向。
逆序对生成排列
对于一个数组 $b$,$b_i$ 表示第 $i$ 个位置的逆序对数量。
我们从左到右进行考虑。
首先写出 $n$,先钦定其为第一个数字。
之后对于下一位如果 $b_2 = 0$ 那么就是放在 $n$ 的左边,不然是右边。
对于第 $k$ 位来说其放置的位置就是原来数列的第 $b_k$ 个位置。
显然这种构造是一个唯一对应一个排列的。
Gray Code
我们发现直接使用二进制进行传输信息使用的空间大小是不确定的,比如说传输 $7 = (111)_2$ 和传输 $8 = (1000)_2$。
格雷码就是构造一种使得相邻数字的二进制编号 $1$ 的个数只相差 $1$ 的编码。而且其只会使用小于最大编号的二进制位,这样的编码就是唯一的。
如何构造?
对于第 $n$ 位可以直接由公式得到 g(n) = n ^ (n >> 1)
。
或者我们考虑从小到大进行构造,也就是对于 $2^{n - 1}$ 的格雷码,我们现在前面全部放 $0$ 之后再在前面放 $1$ 两段一起拼起来。
1 | 0 |
如何翻转格雷码?
1 | int rev_g(int g) { |
后面的生成子集就先鸽一下。