백준 2239 스도쿠 - JAVA

2025. 3. 24. 23:26·알고리즘/백준

2239 문제 링크


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

 

문제 설명


 

입출력


 

코드


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
import java.io.*;
 
public class Main {
    static int[][] sudoku;
    static boolean[][][] visit;
    static boolean success;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringBuilder sb = new StringBuilder();
        
        sudoku = new int[9][9];
// 마지막이 10 인 이유는 스도쿠의 숫자가 들어가는 자리이므로 (1 - base - position)
        visit = new boolean[3][9][10];
        for (int i = 0; i < 9; i++) {
            String input = br.readLine();
            for (int j = 0; j < 9; j++) {
                sudoku[i][j] = input.charAt(j) - '0';
                if (sudoku[i][j] != 0) {
                    visit[0][i][sudoku[i][j]] = true;
                    visit[1][j][sudoku[i][j]] = true;
                    visit[2][(i/3)*3+(j/3)][sudoku[i][j]] = true;
                } 
            } 
        } 
        findNum(0, 0);
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                sb.append(sudoku[i][j]);
            }
            sb.append("\n");
        }
        bw.write(sb.toString());
        bw.flush();
    }
    private static void findNum(int x, int y) {
        if (x == 8 && y == 8) {
            for (int i = 1; i <= 9; i++) {
                if (!visit[0][8][i]) {
                    sudoku[x][y] = i;
                    break;
                } 
            } 
            success = true;
            return;
        } 
        if (sudoku[x][y] != 0) {
            if(y != 8) {
                findNum(x, y+1);
            } else {
                findNum(x+1, 0);
            }
        } else {
            for (int i = 1; i <= 9; i++) {
                if (!visit[0][x][i] && !visit[1][y][i] && !visit[2][(x/3)*3 + (y/3)][i]) {
                    visit[0][x][i] = true;
                    visit[1][y][i] = true;
                    visit[2][(x/3)*3 + (y/3)][i] = true;
                    sudoku[x][y] = i;
                    if (y != 8) {
                        findNum(x, y+1);
                    } else {
                        findNum(x+1, 0);
                    }
                    if (success) {
                        return;
                    } 
                    sudoku[x][y] = 0;
                    visit[0][x][i] = false;
                    visit[1][y][i] = false;
                    visit[2][(x/3)*3+(y/3)][i] =false;
                }
            } 
        }
    }
}
Colored by Color Scripter
cs

 

알고리즘 설명


 

참 어려운 문제였습니다. visit 배열을 3차원 배열로 만들어, visit[0][][]은 열, visit[1][][]은 행, visit[2][][]은 박스로 나누어 행과 열과 3*3박스 배열에 중복되지 않는 수(배열의 인덱스 값이 false인 숫자) 부터 숫자를 채워나가면서 진행이 불가능 할 때, 백트래킹으로 다시 가능한 경우의 수부터 시작하는 알고리즘입니다.

백트래킹의 핵심은 종료점을 명확히 정의하는 것입니다. 이 부분이 생각보다 쉽지 않았습니다... (0,0)부터 시작해서, visit[0]과 visit[1]과 visit[2] 세개의 배열에서 모두 false인 숫자부터 sudoku 배열을 채워나갑니다. 이때, 경우의 수를 위해 백트래킹을 사용하는데, 완성이 불가능하도록 숫자가 채워졌을 때, 종료할 종료점과 스도쿠가 이미 완성되었는데도, 이미 호출되어서 실행되고 있는 함수를 종료할 종료점을 잘 생각해가며 문제를 풀어야 합니다. 백트래킹을 통해 최적의 해를 찾으며, 불가능한 경로는 빠르게 포기하여 불필요한 연산을 최소화하는 것이 이 알고리즘의 핵심입니다. 어렵지만 생각을 깊게하게 해주는 좋은 문제였던 것 같습니다.

 

코드 해석


9×9 스도쿠 판을 입력받아 sudoku[][]배열에 저장하고, 동시에 각 숫자가 속한 행, 열, 그리고 3×3 박스에서 사용된 숫자를 기록하는 visit 배열을 입력에 맞게 초기화합니다. 이후 백트래킹을 이용해 findNum 함수를 호출하여 빈 칸을 채워나갑니다.

 

findNum 함수는 현재 위치 (x, y)가 마지막 칸 (8, 8)에 도달하면 해당 칸에 넣을 수 있는 숫자를 찾고 종료 플래그(success)를 설정한 후 재귀를 종료합니다. 만약 현재 위치가 이미 숫자가 채워져 있는 경우 다음 위치로 이동하며, (y != 8)일 경우 y를 증가시키고 (y == 8)이면 x를 증가시키면서 다음 줄의 첫 번째 칸으로 이동합니다. 빈 칸인 경우 1~9까지의 숫자를 시도하며, 해당 숫자가 현재 행, 열, 3×3 박스에 없는 경우 visit 배열을 갱신하고 숫자를 채운 뒤 재귀 호출을 통해 다음 칸을 채웁니다.

 

만약 이후 진행이 불가능한 경우, 즉 유효한 스도쿠 상태를 만들 수 없는 경우에는 백트래킹을 수행하여 다시 빈 칸으로 되돌리고, visit 배열을 원래 상태로 복구한 뒤 다음 숫자를 시도합니다. 가능한 모든 경우의 수를 탐색하며 유효한 해를 찾으면 최종적으로 완성된 스도쿠 판을 출력한다.

 

코드에서 success가 두 번 사용되는 이유는 스도쿠가 완성된 이후 불필요한 탐색을 방지하기 위함입니다.
첫 번째 success는 마지막 칸 (8, 8)에서 스도쿠를 완성했을 때 true로 설정하여 findNum()함수를 안전하게 종료시키는 용도이고, 두번째 success(64~66줄)는 스도쿠가 이미 완성된 상태임에도, 재귀로 인한 함수 호출을 막는 즉, 불필요한 탐색을 종료시키는 용도라고 생각하면 될것 같습니다.

'알고리즘 > 백준' 카테고리의 다른 글

백준 6523 요세푸스 한 번 더! - JAVA  (0) 2025.04.01
백준 2143 두 배열의 합 - JAVA  (0) 2025.03.25
백준 15681 트리와 쿼리 - JAVA  (0) 2025.03.17
백준 1197 최소 스패닝 트리 - JAVA  (0) 2025.03.06
백준 9465 스티커 - JAVA  (0) 2025.03.03
'알고리즘/백준' 카테고리의 다른 글
  • 백준 6523 요세푸스 한 번 더! - JAVA
  • 백준 2143 두 배열의 합 - JAVA
  • 백준 15681 트리와 쿼리 - JAVA
  • 백준 1197 최소 스패닝 트리 - JAVA
BIS's tech Blog
BIS's tech Blog
Welcome to Kanghyun's tech blog :)
  • BIS's tech Blog
    벼익숙의 기술 블로그
    BIS's tech Blog
  • 전체
    오늘
    어제
    • 분류 전체보기 (63) N
      • 알고리즘 (49) N
        • 백준 (40) N
        • 프로그래머스 (9)
      • CS 스터디 (14)
        • CS - JAVA (11)
        • CS - DB (3)
      • Spring (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    알고리즘 고득점 kit
    DFS
    브루트포스
    깊이 우선 탐색
    BFS
    Top-Down
    java
    너비 우선 탐색
    백준
    Baekjoon
    자료구조
    동적 계획
    기술질문
    재귀
    Lv2
    프로그래머스
    완전탐색
    bottom-up
    dp
    CS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
BIS's tech Blog
백준 2239 스도쿠 - JAVA
상단으로

티스토리툴바