生成排列和子集
这个只是单纯的总结,之后肯定还会搞的(如果没有退役的话)。
生成排列
不妨以生成 1∼n1 \sim n1∼n 的排列进行举例子,我们对于每一个数字的上面先定一个箭头,表示其可以向那边走。
一个数字移动的条件是其箭头指向的相邻的数组比其要小。
例如 1←2←\overset{\leftarrow}{1}\overset{\leftarrow}{2}1←2← 这个时候 222 就是可以向左走一个位置的,也就是意味着下一个排列是 2←1←\overset{\leftarrow}{2}\overset{\leftarrow}{1}2←1←。
我们构造一个排列是从 1←2←…n←\overset{\leftarrow}{1}\overset{\leftarrow}{2} \dots \overset{\leftarrow}{n}1←2←…n← 开始的。
- 每次找到最大的可以移动的数。
- 交换其和其箭头指向的数字。
- 交换所有 i,i>mi, i > mi,i>m 的箭头的方向。
逆序对生成排列
对于一个数组 bbb,bib_ibi 表示第 iii 个位置的逆序对数量。
我们从左到右进行考虑。
首先写出 nnn,先钦定其为第一个数字。
之后对于下一位如果 b2=0b_2 = 0b2=0 那么就是放在 nnn 的左边,不然是右边。
对于第 kkk 位来说其放置的位置就是原来数列的第 bkb_kbk 个位置。
显然这种构造是一个唯一对应一个排列的。
Gray Code
我们发现直接使用二进制进行传输信息使用的空间大小是不确定的,比如说传输 7=(111)27 = (111)_27=(111)2 和传输 8=(1000)28 = (1000)_28=(1000)2。
格雷码就是构造一种使得相邻数字的二进制编号 111 的个数只相差 111 的编码。而且其只会使用小于最大编号的二进制位,这样的编码就是唯一的。
如何构造?
对于第 nnn 位可以直接由公式得到 g(n) = n ^ (n >> 1)。
或者我们考虑从小到大进行构造,也就是对于 2n−12^{n - 1}2n−1 的格雷码,我们现在前面全部放 000 之后再在前面放 111 两段一起拼起来。
0
1
00
01
10
11
000
001
010
011
100
101
110
111
如何翻转格雷码?
int rev_g(int g) {
int res(0);
for(; g; g >>= 1) res ^= g;
return res;
}
后面的生成子集就先鸽一下。
本文介绍了生成排列的基本原理及实现方法,包括通过调整箭头方向来生成排列、利用逆序对生成排列以及格雷码的构造与翻转。同时简述了后续将涉及的子集生成方法。
1065

被折叠的 条评论
为什么被折叠?



