洛谷:5657
OJ: CSPS2019A
根据题目描述的递推构造法:
这个构造出来的序列,恰好就是标准二进制反射格雷码(Binary Reflected Gray Code)。
而这个格雷码的数学生成公式就是:\([\text{Gray}(k) = k \oplus (k >> 1)]\)
它的逆运算(如果你想从格雷码求原编号)是:\([k = g \oplus (g >> 1) \oplus (g >> 2) \oplus \dots]\)
| k | 二进制 | k>>1 | XOR结果 g | 格雷码 |
|---|---|---|---|---|
| 0 | 00 | 00 | 00 | 00 |
| 1 | 01 | 00 | 01 | 01 |
| 2 | 10 | 01 | 11 | 11 |
| 3 | 11 | 01 | 10 | 10 |
对应题目 2 位格雷码的排列正是:
00, 01, 11, 10
完美匹配。
是的,g = k ^ (k >> 1) 这个公式不仅能用,而且是标准、快速、通用的格雷码生成方法。
它可以直接用于任意 n 位格雷码题目,无需递归构造。
取第i位操作
(g >> i) & 1ULL
它是 C/C++ 位运算 中非常常见的“取某一位”的写法。假设我们有一个无符号整数 g(例如格雷码结果)。我们想取出 g 的第 i 位(二进制位)。
① (g >> i)
👉 表示将 g 右移 i 位。
也就是把第 i 位移到最低位(第 0 位)。
例如:
g = 110110 (二进制)
i = 2
g >> 2 = 1101
原来的第 2 位移到了末尾位置)
& 1ULL👉 & 是按位与(bitwise AND)运算。1ULL 表示一个 值为 1 的无符号长整型常量(unsigned long long)。
1ULL 的二进制形式是:
...00000001
按位与的规则:
所以 (g >> i) & 1ULL 的作用是:
取出 g 的第 i 位(二进制位),结果是 0 或 1。
假设:
unsigned long long g = 0b101011; // 十进制 43
| i | 表达式 (g >> i) & 1ULL | 结果 | 含义 |
|---|---|---|---|
| 0 | (101011 >> 0) & 1 = 101011 & 1 | 1 | 第 0 位是 1 |
| 1 | (101011 >> 1) & 1 = 10101 & 1 | 1 | 第 1 位是 1 |
| 2 | (101011 >> 2) & 1 = 1010 & 1 | 0 | 第 2 位是 0 |
| 3 | (101011 >> 3) & 1 = 101 & 1 | 1 | 第 3 位是 1 |
| 4 | (101011 >> 4) & 1 = 10 & 1 | 0 | 第 4 位是 0 |
| 5 | (101011 >> 5) & 1 = 1 & 1 | 1 | 第 5 位是 1 |
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /**************************************************************** * 代码作者: Alex Li * 创建时间: 2025-10-05 21:20:23 * 最后修改: 2025-10-05 21:47:58 * 文件描述: 【19CSPS提高组】格雷码 ****************************************************************/ #include <bits/stdc++.h> using namespace std; int main() { long long n; // 位数 unsigned long long k; // 第 k 个(从 0 开始) if (!(cin >> n >> k)) return 0; unsigned long long g = k ^ (k >> 1); // k 的格雷码 // 输出为 n 位二进制串(高位在前,补前导 0) for (long long i = n - 1; i >= 0; --i) { cout << ((g >> i) & 1ULL); } return 0; } |