[백준] 1307 (마방진)
#PS#BOJ#tier:p4#math#implementation#ad_hoc

[백준] 1307 (마방진)

마방진(Magic Square)이란 N×N 격자에 1부터 N²까지의 정수를 하나씩 배치하여 모든 가로줄, 세로줄, 대각선의 합이 동일한 배치를 말합니다. 이때 공통 합을 매직 상수(Magic Constant)라 하며, 그 값은 M = N(N² + 1) / 2입니다.

N의 형태에 따라 구성 방법이 달라집니다. 이 글에서는 홀수 N,4의 배수 N,4k+2 형태의 N세 가지 경우를 각각 다룹니다.

홀수 마방진 (N = 2k+1)

알고리즘: Siamese (de la Loubère) 방법

17세기 프랑스 외교관 Simon de la Loubère가 태국에서 배워온 방법입니다. 홀수 크기 마방진을 구성하는 가장 고전적이고 직관적인 방법으로, 단순한 규칙 두 가지만으로 완전한 마방진을 만들어냅니다.

규칙 1: 기본 이동 ↗

현재 위치에서 한 칸 위, 한 칸 오른쪽으로 이동합니다. 격자 바깥으로 나가면 반대편으로 되돌아옵니다 (토러스 구조).

규칙 2: 충돌 시 ↓

이동하려는 칸에 이미 숫자가 있으면, 대신 원래 위치에서 한 칸 아래로 이동합니다.

# 시작: 첫 행의 가운데 열에 1 배치
x, y = 0, n // 2

for i in 1 to n*n:
    grid[x][y] = i
    nx, ny = (x-1) % n, (y+1) % n  # ↗ 이동
    if grid[nx][ny] != 0:           # 충돌!
        x = (x+1) % n              # ↓ 아래로
    else:
        x, y = nx, ny

인터랙티브 시뮬레이터

숫자를 하나씩 배치하는 과정을 관찰해 보세요. ↗ 방향으로 이동하다가 충돌하면 ↓로 내려갑니다.

크기:Step 1 / 25
1
Step 11을 (0, 2)에 배치 — 첫 행 가운데에서 시작
속도:

4N 마방진 (N = 4k)

알고리즘: 십자 교환법 (Cross-Swap)

N이 4의 배수일 때 사용하는 간결한 방법입니다. 핵심 아이디어는 1~N²을 순서대로 채운 뒤, 특정 영역의 셀을 점대칭 위치와 교환하는 것입니다.

유지 영역

4개의 코너 블록 (각 N/4 × N/4) 과 중앙 블록 (N/2 × N/2) 은 원래 값을 유지합니다.

교환 영역 (십자)

코너와 중앙 사이를 잇는 십자(+) 모양 영역의 셀 (i, j)를 점대칭 위치 (N-1-i, N-1-j)와 교환합니다.

·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·

8×8 교환 패턴: = 교환, · = 코너 유지, · = 중앙 유지

# 1~N² 순서대로 채우기
ans[i][j] = i * N + j + 1

# 십자(cross) 영역을 점대칭 교환
for i in range(N // 4):              # 상단 Q행
    for j in range(N//4, 3*N//4):    # 중앙 열
        swap(ans[i][j], ans[N-1-i][N-1-j])     # 상↔하
        swap(ans[j][i], ans[N-1-j][N-1-i])     # 좌↔우

왜 동작하는가?

순서대로 채운 격자에서 (i, j)의 값과 점대칭 위치 (N-1-i, N-1-j)의 값의 합은 항상 N² + 1입니다. 교환 시 두 위치를 포함하는 행·열 쌍의 합이 보존되며, 십자 패턴이 각 행·열에서 정확히 절반의 셀만 교환되도록 보장합니다.

인터랙티브 시뮬레이터

순서대로 채운 뒤, 코너·중앙은 유지하고 십자(cross) 영역만 점대칭 교환합니다.

크기:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Phase 1/3초기 상태: 1부터 N²까지 순서대로 채움

4N+2 마방진 (N = 4k+2)

알고리즘: Strachey 방법

N = 4k+2 (예: 6, 10, 14, …) 형태는 홀수도 아니고 4의 배수도 아닌 가장 까다로운 경우입니다. Strachey 방법은 n = N/2 크기의 홀수 마방진을 기반으로, 각 사분면에 서로 다른 오프셋을 더하는 합성 방법입니다.

Step 1

홀수 마방진 구성

n = N/2 크기의 홀수 마방진 M을 만듭니다 (값: 1~n²).

Step 2

오프셋 승수 결정

N×N의 각 셀에 0, 1, 2, 3 중 하나의 승수를 배정합니다.

Step 3

최종 합산

ans[i][j] = 승수 × n² + M[i%n][j%n]으로 값을 결정합니다.

오프셋 승수 배치 규칙:

# 상반부 (i < N/2): k = [3, 0, 2, 1]
# 하반부 (i ≥ N/2): k = [0, 3, 1, 2]
#
# 좌측 절반 (cols 0..N/2-1):
#   일반 행: [0, N/4)열 → k[0],  [N/4, N/2)열 → k[1]
#   특수 행 (i % (N/2) == N/4):
#     0열 → k[1], [1, 1+N/4)열 → k[0], 나머지 → k[1]
#
# 우측 절반 (cols N/2..N-1):
#   [N/2, N-N/4]열 → k[2],  [N-N/4+1, N)열 → k[3]
#
# 최종: ans[i][j] = 승수[i][j] × n² + M[i%n][j%n]

왜 동작하는가?

오프셋 배치는 각 행·열에서 승수 0, 1, 2, 3이 각각 정확히 n번(= N/2번) 등장하도록 설계되었습니다. 따라서 오프셋의 행/열 합이 균일해지고, 4개 사분면에 복사된 홀수 마방진의 합 성질도 보존되므로 전체가 마방진이 됩니다.

인터랙티브 시뮬레이터

홀수 마방진을 4개 사분면에 복사한 뒤, 오프셋 패턴에 따라 값을 더합니다.

크기:
8
1
6
3
5
7
4
9
2

3×3 홀수 마방진

Phase 1/33×3 홀수 마방진을 먼저 구성 (1~9)

정리

세 가지 방법 요약

홀수 (2k+1)

Siamese

O(N²)

예: 3, 5, 7, 9, …

4의 배수 (4k)

십자 교환

O(N²)

예: 4, 8, 12, 16, …

4k+2

Strachey

O(N²)

예: 6, 10, 14, 18, …

핵심 포인트

세 방법 모두 시간·공간 복잡도가 O(N²)이며, N ≤ 300 범위에서 충분히 빠릅니다. 입력 N의 형태(홀수 / 4의 배수 / 4k+2)에 따라 적절한 방법을 선택하면 됩니다.

구현

코드 구조

n = N  # N은 홀수
odd = [[0] * n for _ in range(n)]
x, y = 0, n // 2          # 첫 행 가운데에서 시작

for i in range(1, n * n + 1):
    odd[x][y] = i
    nx = (x - 1) % n       # ↗ 위로
    ny = (y + 1) % n       # ↗ 오른쪽으로
    if odd[nx][ny] != 0:    # 충돌 시
        x = (x + 1) % n    # 대신 ↓ 아래로
    else:
        x, y = nx, ny

Siamese 방법의 핵심 루프입니다. 각 숫자를 배치한 뒤 ↗ 이동을 시도하고, 이미 숫자가 있으면 ↓로 방향을 바꿉니다.

테스트

직접 테스트해 보기

원하는 크기를 입력하면 적절한 방법이 자동으로 선택됩니다.

N =홀수 (Siamese)
17
24
1
8
15
23
5
7
14
16
4
6
13
20
22
10
12
19
21
3
11
18
25
2
9
매직 상수 = 65 — 모든 합이 일치합니다!