【题目链接】
【题目考点】
1. 递归
2. 位运算
3. 格雷码
【解题思路】
解法1:递归
递归问题:求n位格雷码中的第k号二进制串
递归关系:
根据题目描述:
n位格雷码
- 由n-1位格雷码的 2 n − 1 2^{n-1} 2n−1个二进制串按顺序排列加前缀0,构成前 2 n − 1 2^{n-1} 2n−1个格雷码,编号为 0 ∼ 2 n − 1 − 1 0\sim2^{n-1}-1 0∼2n−1−1
- 由n-1位格雷码的 2 n − 1 2^{n-1} 2n−1个二进制串按逆序排列加前缀1,构成后 2 n − 1 2^{n-1} 2n−1个格雷码,编号为 2 n − 1 ∼ 2 n − 1 2^{n-1}\sim2^n-1 2n−1∼2n−1
要查询的是n位格雷码的第k号二进制串,先比较k与 2 n − 1 2^{n-1} 2n−1
- 如果 k < 2 n − 1 k < 2^{n-1} k<2n−1,则查找n-1位格雷码中的第k号二进制串,在前面再加上前缀0,即为n位格雷码的第k号二进制串。
- 如果
k
≥
2
n
−
1
k \ge 2^{n-1}
k≥2n−1,n位格雷码第k号二进制串是编号
2
n
−
1
∼
2
n
−
1
2^{n-1}\sim2^n-1
2n−1∼2n−1的格雷码中的第
k
−
2
n
−
1
k-2^{n-1}
k−2n−1个数字(从0开始)。
该二进制串去掉前缀1后,为在n-1位格雷码的逆序序列中的第 k − 2 n − 1 k-2^{n-1} k−2n−1个数字
推导对于正向、逆向都从0开始数的长为n的序列,逆向第i个数字是正向第几个数字
逆向 正向 0 n-1 1 n-2 2 n-3 … … n-1 0 i n-1-i
因此,在n-1位格雷码的有
2
n
−
1
2^{n-1}
2n−1个元素的逆序序列中的第
k
−
2
n
−
1
k-2^{n-1}
k−2n−1个数字,在正序序列中编号为
2
n
−
1
−
1
−
(
k
−
2
n
−
1
)
=
2
n
−
1
−
k
2^{n-1}-1-(k-2^{n-1})=2^n-1-k
2n−1−1−(k−2n−1)=2n−1−k
(代码中将该表达式写为
2
n
−
1
−
k
−
1
+
2
n
−
1
2^{n-1}-k-1+2^{n-1}
2n−1−k−1+2n−1,以避免当n为64时,数据值超过unsigned long long可以表示的范围)
查找n-1位格雷码中的第
2
n
−
1
−
k
2^n-1-k
2n−1−k号二进制串,在前面再加上前缀1,即为n位格雷码的第k号二进制串。
递归出口:当n为1时,只有1位格雷码。k为0时,第0号格雷码为"0"。k为1时,第1号格雷码为1。
【注意】:n最大为64,且
k
<
2
n
k<2^n
k<2n,那么k最大为
2
64
−
1
2^{64}-1
264−1,应该使用unsigned long long类型来保存k以及二进制串对应的数值。
为了方便得到2的幂,可以先使用递推方法预处理出一个数组保存2的幂。
解法2:二进制转为格雷码
如将k转为二进制数后共有m位,从低位到高位的编号为0, 1, …, m-1。
例:k为13,二进制编码为:1101,
第3位为1,第2位为1,第1位为0,第0位为1
- 格雷码的第n-1位与二进制数的第n-1位相同
- i<n-1时,格雷码第i位 = 二进制数第i位 ⊕ \oplus ⊕ 二进制数第i+1位( ⊕ \oplus ⊕是异或运算)
将数值k右移1位,得到k1。k1的第i位就是k的第i+1位。
将k与k1异或,即可得到格雷码。
【题解代码】
解法1:递归
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
ULL pow2[70];//pow2[i]:2^i的值
string solve(ULL n, ULL k)//求n位格雷码中的第k号二进制串
{
if(n == 1)//1位格雷码
{
if(k == 0)
return "0";
else//k == 1
return "1";
}
if(k < pow2[n-1])
return "0"+solve(n-1, k);//查找n-1位格雷码中的第k号二进制串,在前面再加上前缀0
else//k >= pow2[n-1]
return "1"+solve(n-1, pow2[n-1]-k-1+pow2[n-1]);//查找n-1位格雷码中的第2^n-1-k2号二进制串,在前面再加上前缀1
}
void initPow2()//预处理生成2的幂
{
pow2[0] = 1;
for(int i = 1; i <= 63; ++i)
pow2[i] = pow2[i-1]*2;
}
int main()
{
initPow2();
ULL n, k;
cin >> n >> k;
cout << solve(n, k);
return 0;
}
解法2:二进制转为格雷码
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
int main()
{
string s;
ULL n, k, a;
cin >> n >> k;
a = k ^ (k >> 1);
for(int i = 1; i <= n; ++i)//格雷码必须有n位,不足则前面补0
{
s = char(a%2+'0')+s;
a /= 2;
}
cout << s;
return 0;
}