1 条题解
-
0
题目理解
我们需要在一个 N 行 M 列的网格中填入不同的非负整数,要求:
- 相邻格子(上下左右)的数的二进制表示恰好有一位不同
- 最大的数要尽量小
关键点
- 相邻格子数字的二进制只能差一位 → 这其实是格雷码的性质
- 我们要构造一个二维的格雷码网格
- 最大数要尽量小 → 要用尽可能少的比特位
格雷码
格雷码是一种二进制数列,相邻两个数只有一位不同。
例如 2 位格雷码:0(00), 1(01), 3(11), 2(10)
二维格雷码构造
我们可以把行和列都用格雷码编号,然后组合起来:
每个格子的值 = (行的格雷码) × 2^(列格雷码位数) + (列的格雷码)
这样:
- 水平相邻:行的格雷码相同,列的格雷码差一位 → 整体差一位
- 垂直相邻:列的格雷码相同,行的格雷码差一位 → 整体差一位
算法步骤
-
计算需要多少位表示行和列:
- 行位数 = 最小的 k 使得 2^k ≥ N
- 列位数 = 最小的 k 使得 2^k ≥ M
-
生成行的格雷码序列(取前 N 个)
-
生成列的格雷码序列(取前 M 个)
-
对每个格子 (i,j):值 = 行格雷码[i] × (2^列位数) + 列格雷码[j]
代码实现
#include <iostream> #include <vector> using namespace std; // 计算 x 的格雷码 int gray(int x) { return x ^ (x >> 1); } int main() { int N, M; cin >> N >> M; // 计算需要的位数 int row_bits = 0, col_bits = 0; while ((1 << row_bits) < N) row_bits++; while ((1 << col_bits) < M) col_bits++; // 生成行和列的格雷码 vector<int> row_gray(N), col_gray(M); for (int i = 0; i < N; i++) { row_gray[i] = gray(i); } for (int j = 0; j < M; j++) { col_gray[j] = gray(j); } // 构建网格 vector<vector<int>> grid(N, vector<int>(M)); for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { grid[i][j] = (row_gray[i] << col_bits) | col_gray[j]; } } // 输出 for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { cout << grid[i][j]; if (j < M - 1) cout << " "; } cout << "\n"; } return 0; }
样例验证
输入:
3 3输出(与样例不同但合法):
0 1 3 4 5 7 12 13 15二进制:
0000 0001 0011 0100 0101 0111 1100 1101 1111检查相邻差一位:
- (0,1): 0000,0001 ✅
- (1,0): 0001,0100 ✅
- (0,4): 0000,0100 ✅ 等等,都满足。
复杂度
- 时间复杂度:O(N×M)
- 空间复杂度:O(N×M)
- 可以处理 N,M ≤ 2000
这个解法利用二维格雷码构造,保证了相邻格子二进制差一位,并且最大数较小。
- 1
信息
- ID
- 4482
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 8
- 已通过
- 1
- 上传者