Post

(백준) 2580번 - 스토쿠

(백준) 2580번 - 스토쿠

(백준) 2580번 - 스토쿠

문제

https://www.acmicpc.net/problem/2580

풀이

행, 열, 3 X 3 영역에 중복된 숫자가 존재하지 않도록 백트래킹으로 수를 배치하는 문제이다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

struct Coor {
    int x, y;
    Coor(int x, int y) : x(x), y(y) {}
};

int arr[9][9];
vector<Coor> zeros;

void printArr() {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cout << arr[i][j] << ' ';
        }
        cout << '\n';
    }
}

bool row(int num, int x, int y) {
    for (int i = 0; i < 9; i++) {
        if (arr[x][i] == num) {
            return false;
        }
    }
    return true;
}

bool col(int num, int x, int y) {
    for (int i = 0; i < 9; i++) {
        if (arr[i][y] == num) {
            return false;
        }
    }
    return true;
}

bool square(int num, int x, int y) {
    int sx = (x / 3) * 3;
    int sy = (y / 3) * 3;

    for (int i = sx; i < sx + 3; i++) {
        for (int j = sy; j < sy + 3; j++) {
            if (arr[i][j] == num) {
                return false;
            }
        }
    }
    return true;
}

bool ok(int num, int x, int y) {
    return col(num, x, y) && row(num, x, y) && square(num, x, y);
}

void dfs(int cnt) {
    if (cnt == zeros.size()) {
        printArr();
        exit(0);
    }

    Coor cur = zeros[cnt];

    for (int i = 1; i <= 9; i++) {
        if (ok(i, cur.x, cur.y)) {
            arr[cur.x][cur.y] = i;
            dfs(cnt + 1);
            arr[cur.x][cur.y] = 0;
        }
    }
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> arr[i][j];
            if (arr[i][j] == 0) {
                zeros.push_back(Coor(i, j));
            }
        }
    }

    dfs(0);
}

단, 방문 여부를 비트마스킹을 사용한다면 아래와 같이 작성할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

struct Coor {
    int x, y;
    Coor(int x, int y) : x(x), y(y) {}
};

int arr[9][9];
vector<Coor> zeros;

int chk_row[9]; // i번째 행에 방문 체크 비트
int chk_col[9];
int chk_square[9];

int get_square_idx(int x, int y) {
    return (x / 3) * 3 + (y / 3);
}

void printArr() {
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cout << arr[i][j] << ' ';
        }
        cout << '\n';
    }
}

void dfs(int cnt) {
    if (cnt == zeros.size()) {
        printArr();
        exit(0);
    }

    int x = zeros[cnt].x;
    int y = zeros[cnt].y;
    int square_idx = get_square_idx(x, y);

    for (int i = 1; i <= 9; i++) {
        int mask = chk_row[x] | chk_col[y] | chk_square[square_idx];
        int cur = 1 << (i - 1);

        if ((mask & cur) == 0) {
            arr[x][y] = i;
            chk_row[x] ^= cur;
            chk_col[y] ^= cur;
            chk_square[square_idx] ^= cur;

            dfs(cnt + 1);

            arr[x][y] = 0;
            chk_row[x] ^= cur;
            chk_col[y] ^= cur;
            chk_square[square_idx] ^= cur;
        }
    }
}

int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> arr[i][j];
            if (arr[i][j] == 0) {
                zeros.push_back(Coor(i, j));
            }
            else {
                int num = arr[i][j];
                int cur = 1 << (num - 1);
                int square_idx = get_square_idx(i, j);

                chk_row[i] |= cur;
                chk_col[j] |= cur;
                chk_square[square_idx] |= cur;
            }
        }
    }

    dfs(0);
}

변수는 따로 주어지지 않았지만, 스토쿠 변의 길이를 $N$, 0의 개수를 $M$으로 가정하자.

시간복잡도

첫 번째 코드는 매 단계에서 행, 열, 3 X 3 영역을 검사한다. 이러한 검사 비용은 $O(3N)$이다. 또한 최악의 경우 $M$개의 빈 칸에 1 ~ 9까지의 숫자를 넣어보므로, $O(9^M)$이다. 따라서 전체 시간복잡도는 $O(N \cdot 9^M)$이다.

두 번째 코드는 반복문 없이 유효성을 검사하므로, 검사 비용은 $O(1)$이다. 단, 역시 탐색 과정에서 시간복잡도는 $O(9^M)$이다. 따라서 전체 시간복잡도는 $O(9^M)$이다.

공간복잡도

첫 번째 코드는 arr, zeros 벡터에서 $O(N^2)$, 재귀 스택의 깊이는 빈 칸의 개수와 동일하므로 $O(M)$이다. $N^2 \geq M$이므로 전체 공간복잡도는 $O(N^2)$이다.

두 번째 코드 역시 재귀 스택 깊이가 동일하므로 전체 공간복잡도는 $O(N^2)$이다.

This post is licensed under CC BY 4.0 by the author.