QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: Hussein

Posted at: 2026-02-09 11:34:22

Last updated: 2026-02-09 11:59:25

Back to Problem

#15207. 2D Gray Code 题解

思路

我们可以打表找规律:

  • 当 $n = 2$ 时,输出:
    0 1 3 2 
    4 5 7 6 
    12 13 15 14 
    8 9 11 10
  • 当 $n = 3$ 时,输出:
    0 1 3 2 6 7 5 4 
    8 9 11 10 14 15 13 12 
    24 25 27 26 30 31 29 28 
    16 17 19 18 22 23 21 20 
    48 49 51 50 54 55 53 52 
    56 57 59 58 62 63 61 60 
    40 41 43 42 46 47 45 44 
    32 33 35 34 38 39 37 36
  • 当 $n = 4$ 时,输出:
    0 1 3 2 6 7 5 4 12 13 15 14 10 11 9 8 
    16 17 19 18 22 23 21 20 28 29 31 30 26 27 25 24 
    48 49 51 50 54 55 53 52 60 61 63 62 58 59 57 56 
    32 33 35 34 38 39 37 36 44 45 47 46 42 43 41 40 
    96 97 99 98 102 103 101 100 108 109 111 110 106 107 105 104 
    112 113 115 114 118 119 117 116 124 125 127 126 122 123 121 120 
    80 81 83 82 86 87 85 84 92 93 95 94 90 91 89 88 
    64 65 67 66 70 71 69 68 76 77 79 78 74 75 73 72 
    192 193 195 194 198 199 197 196 204 205 207 206 202 203 201 200 
    208 209 211 210 214 215 213 212 220 221 223 222 218 219 217 216 
    240 241 243 242 246 247 245 244 252 253 255 254 250 251 249 248 
    224 225 227 226 230 231 229 228 236 237 239 238 234 235 233 232 
    160 161 163 162 166 167 165 164 172 173 175 174 170 171 169 168 
    176 177 179 178 182 183 181 180 188 189 191 190 186 187 185 184 
    144 145 147 146 150 151 149 148 156 157 159 158 154 155 153 152 
    128 129 131 130 134 135 133 132 140 141 143 142 138 139 137 136
  • 当 $n = 5$ 时……
神秘规律

我们可以发现一些神秘规律:

  1. 第一行的前几个数都是一样的
  2. 每一行的相对顺序都是一样的
  3. 每一行和每一列的相对大小顺序是一样的
  4. 第一行就是一位格雷码的一段前缀
一维格雷码

通过上面说的神秘规律让我们回忆起一维格雷码。

一维格雷码的构造方法是:

初始有个二进制数列为 $a = [0, 1]$,接下来我们不断按顺序做以下操作:

  1. 令 $b = a$,翻转 $b$;即:$b = [0, 1, 3, 2]$,翻转 $b$ 后,$b = [2, 3, 1, 0]$;
  2. 设 $x + y (x, y 为一个数列)$ 为把 $y$ 拼接在 $x$ 后面的数列(不排序);
  3. $a := a + b$。

这样我们再打个表:

给定 $n$,输出一维格雷码前 $2^n$ 的数。

我们直接令 $n = 4$,输出:

0 1 3 2 6 7 5 4 12 13 15 14 10 11 9 8

我们发现如果我们对这个数列每四个数换个行,则会有:

0 1 3 2 
6 7 5 4 
12 13 15 14 
10 11 9 8
二维格雷码

不难发现,奇数行与我们的二维格雷码的奇数行一模一样,偶数行是正好相反的。

那我们直接把偶数行翻转一下,输出就行了。

AC Code

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define sort stable_sort
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int, int> PII;
typedef double db;
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a;}
ll lcm(ll a, ll b) { return a / gcd(a, b) * b;}
ll quick_pow(ll a, ll b, const ll p) { if (b == 1) { return a * b % p;} if (b == 0) { return 1 % p;} ll x = quick_pow(a, b / 2, p); if (b & 1) { return (1ll * x * x % p * a) % p;} return (1ll * x * x) % p;}
const ll mod = 1e9 + 7;
// head

const ll N = 10;
int TC = 1, n, a[1 << N][1 << N], c[1 << (2 * N)], b[1 << (2 * N)];

inline void solve() {
    scanf("%d", &n);
    vector<int> v(2);
    v[0] = 0, v[1] = 1;
    for (int i = 1; i <= 16; i++) {
        VI w = v;
        reverse(all(w));
        for (auto &j : w) {
            j += 1 << i;
            v.pb(j);
        }
    }
    int cnt = 1, l = 1;
    for (auto i : v) {
        if (l > 1 << n) 
            break;
        a[l][cnt] = i;
        cnt++;
        if (cnt > 1 << n) {
            cnt = 1;
            l++;
        }
    }
    for (int i = 1; i <= 1 << n; i++) {
        if (i % 2 == 0) 
            reverse(a[i] + 1, a[i] + (1 << n) + 1);
        for (int j = 1; j <= 1 << n; j++) 
            printf("%d ", a[i][j]);
        puts("");
    }
}

int main() {
//    scanf("%d", &TC);
    while (TC--) {
        solve();
    }
}

Comments

No comments yet.