알고리즘/백트래킹

[백준 2580] - [백트래킹] - 스도쿠

팡스 2018. 12. 21. 17:58

문제링크 : https://www.acmicpc.net/problem/2580



이문제는 백트래킹으로 접근 할 수 있는 문제이다.


예전에 풀었던 N-Queen 문제도 백트래킹이었는데 간단하게 설명하자면 


현재 인덱스에 값을 넣어보고 다음 탐색을 돌린다. 다시 현재 인덱스에는 값을 초기화 시킨다.


arr[y][x] = i;

backtracking(idx+1, cnt+1);

arr[y][x] = 0;



위에 소스처럼 백트래킹을 진행하는데 이렇게 하면 결국엔 다 초기화 되는거 아냐?? 하겠지만 잘 생각해보면 그렇지 않다.


최종 arr의 값은 당연히 초기 탐색할때의 값이 되겠지만 탐색을 하는 도중에 맞는 결과가 나왔을거고 


맞는 결과가 나왔을때 배열을 바로 출력을 했다. 


이해가 안된다면 백트래킹에 대한 설명 N-Queen 문제를 풀어보고 이문제를 접근하면 접근법이 보일것이라 생각한다.


2018/11/21 - [알고리즘/백트래킹] - [백준-9663]-[백트래킹] - N-Queen



문제를 푸는 방법은 값을 입력 받을때 0을 입력받으면 좌표를 배열에 add 한다.


배열에 add 한 좌표만 탐색하면 되기 때문에 list 에 add 한다.


그다음 1 부터 9까지 입력 가능한 수를 해당 좌표에 대입 시키고 

해당 좌표에 X 값이 위치했을때 성립하는지 같은 함수를 호출 해서 다음  탐색을 진행한다.


그렇게 된다면 전부 입력되는 한 순간이 나오고 그 때 배열을 출력 시키고 결과가 출력 됐으니 더이상 진행 하지 말라고 isEnd 라는 플래그를 true로 변경하여


더이상의 다른 불 필요한 탐색을 진행 못하게 막으면 된다.


소스


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
92
93
94
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
 
public class Main {
    public static final int size = 9;
    public static int[][] arr;
    
    public static List<int[]> list;
    public static boolean isEnd;
    
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        
        StringTokenizer st = null;
        
        arr = new int[size][size];
        list = new ArrayList<>();
        
        for(int i=0; i < size; i++) {
            st = new StringTokenizer(br.readLine());
            
            for(int j=0; j < size; j++) {
                arr[i][j] = Integer.parseInt(st.nextToken());
                
                if(arr[i][j] == 0) list.add(new int[] {i, j});
            }
        }
        // 입력 완료 
        backtracking(00);
        
    }
    public static void backtracking(int idx, int cnt) {
        // 결과가 나왔다면 조회할 필요가 없다.
        if(isEnd) return;
        
        //빈칸에 입력된 cnt가 초기 zero 값의 cnt와 같다면 전부 입력이 된것.
        if(cnt == list.size()) {
            isEnd = true;
            print();
            return;
        }
        // 인덱스가 list크기를 넘어가면 종
        if(idx >= list.size()) return;
        
        int y = list.get(idx)[0];
        int x = list.get(idx)[1];
        
        // 1 부터 9 까지 입력 가능한 숫자 를 확인한다.
        for(int i=1; i<=9; i++) {
            // 해당 숫자가 가능하면 해당 좌표에 값을 입력하고 backtracking을 한다. 
            if(isAble(y, x, i)) {
                arr[y][x] = i;
                backtracking(idx+1, cnt+1);
                arr[y][x] = 0;
            }
        }
        
    }
    
    public static void print() {
        // 결과 출력
                StringBuilder sb = new StringBuilder();
                for(int i=0; i < size; i++) {
                    for(int j=0; j < size; j++) {
                        sb.append(arr[i][j] + " ");
                    }
                    sb.append("\n");
                }
                System.out.println(sb.toString());
    }
    public static boolean isAble(int y, int x, int v) {
        for(int i=0; i < size; i++) {
            // 가로가 유효한지 arr[y][x]의 값이 가로에 또 존재하면 안된다.
            if(arr[y][i] == v) return false;
            
            // 세로가 유효한지 arr[y][x]의 값이 세로에 또 존재하면 안된다.
            if(arr[i][x] == v) return false;
        }
        
        // 3*3이 유효한지
        int y_s = y/3 * 3;
        int x_s = x/3 * 3;
        for(int i = y_s; i < y_s+3; i++) {
            for(int j = x_s; j < x_s+3; j++) {
                if(arr[i][j] == v) return false;
            }
        }
        return true;
    }
}
cs