백준 | 2580번. 스도쿠 - 백트래킹(DFS) 문풀

728x90

⬛ 백준 2580번. 스도쿠 - 백트래킹(DFS) 문풀

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

 

2580번: 스도쿠

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루

www.acmicpc.net

문제

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 일부 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.

문제 그림

나머지 빈 칸을 채우는 방식은 다음과 같다.

  1. 각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
  2. 굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.

위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.

설명 1

또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.

설명 2

이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.

완성 그림

게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.

입력

아홉 줄에 걸쳐 한 줄에 9개씩 게임 시작 전 스도쿠판 각 줄에 쓰여 있는 숫자가 한 칸씩 띄워서 차례로 주어진다. 스도쿠 판의 빈 칸의 경우에는 0이 주어진다. 스도쿠 판을 규칙대로 채울 수 없는 경우의 입력은 주어지지 않는다.

출력

모든 빈 칸이 채워진 스도쿠 판의 최종 모습을 아홉 줄에 걸쳐 한 줄에 9개씩 한 칸씩 띄워서 출력한다.

스도쿠 판을 채우는 방법이 여럿인 경우는 그 중 하나만을 출력한다.


💚 문제 접근 방식

이 문제 역시 푸는 과정에서 어려움을 겪어서 이해한 풀이 방식을 정리했다.
[실패 지점] : 3X3 안에 소속된 행, 열에 대하여 중복값을 확인하는 부분을 어떤 식으로 구현해야 할지 막혔고, DFS 복귀 시점을 정확하게 두지 못해서 계속 실패가 떴던 것 같다.

 

스도쿠 게임 9X9 안에서 같은 행에도, 같은 열에도, 같은 3X3칸 안에서도 동일한 값이 중복되지 않도록 수를 배치하는 게임이다.

  • DFS로 행과 열에 대해서 쭉 깊이 탐색 하도록 풀어야 한다.

1) DFS 에서 만약 현재 col(열) 값이 9까지 온다면, 다음 행에 대한 탐색을 시도 해야 한다.

2) DFS 에서 만약 현재 row(행)의 값이 9까지 온다면, 모든 9X9를 탐색한 것이므로 map[][] 배열을 차례대로 출력한 뒤 System.exit(0)으로 아예 종료시킨다.

3) isPossible(row, col, val)로 확인해야 할 것은 빈칸에 들어갈 조건을 만족하는 값인지 여부다.

   (a). 같은 행에 대해 동일한 val 값이 없는지 확인

    (b). 같은 열에 대해 동일한 val 값이 없는지 확인

    (c). 해당 row, col이 속한 3 X 3 상자 안에 동일한 val 값이 없는 지 확인

0 / 3 = 0   3 / 3 = 1    6 / 3 = 2
1 / 3 = 0   4 / 3 = 1    7 / 3 = 2
2 / 3 = 0   5 / 3 = 1    8 / 3 = 2 

Q. 이 코드에서 map[row][col] = 0을 주는 이유

if(map[row][col] == 0) {
			//빈값 매꾸기 
			for(int val=1; val<= 9; val++) {
				if(isPossible(row, col, val)) {
					map[row][col] = val;
					DFS(row, col+1);//다음 열에 대해 조사 
				}
			}
			//무언가 문제가 발생한 경우 
			map[row][col] = 0;//다시 두고 
			return;//여기서 복귀 
		}
		
		DFS(row, col+1);

스도쿠 해결 알고리즘에서는 arr[row][col] = 0이 역추적에 사용됩니다.

  • 스도쿠 그리드를 채우려고 할 때 알고리즘이 셀에 배치할 유효한 숫자(1~9)를 찾을 수 없는 지점에 도달하면 이전에 선택한 경로가 잘못되었음을 의미합니다. 따라서 값을 0으로 설정하여 이전 셀로 역추적하고 다른 숫자를 시도하여 다른 가능성을 탐색합니다. 이 단계는 알고리즘이 퍼즐의 다양한 부분을 탐색하고 막다른 골목에 직면했을 때 다양한 선택을 할 수 있도록 해주기 때문에 매우 중요합니다. 셀 값을 0으로 재설정하면 알고리즘은 후속 반복 중에 해당 셀에서 대체 숫자를 시도할 수 있도록 보장합니다.

💚 제출 코드

import java.util.Scanner;

/**
 * 2580번. 스도쿠 - 백트래킹 
 */
public class Main {
	static int[][] map;
	//DFS
	static void DFS(int row, int col) {
		if(col == 9) {
			//다음 행에 대한 깊이 탐색
			DFS(row+1, 0);
			return;
		}
		
		if(row == 9) {
			for(int i=0; i<9; i++) {
				for(int j=0; j<9; j++) {
					System.out.print(map[i][j] + " ");
				}
				System.out.println();
			}
			System.exit(0);
		}
		
		
		if(map[row][col] == 0) {
			//빈값 매꾸기 
			for(int val=1; val<= 9; val++) {
				if(isPossible(row, col, val)) {
					map[row][col] = val;
					DFS(row, col+1);//다음 열에 대해 조사 
				}
			}
			//무언가 문제가 발생한 경우 
			map[row][col] = 0;//다시 두고 
			return;//여기서 복귀 
		}
		
		DFS(row, col+1);
	}
	
	//isPossible
	static boolean isPossible(int row, int col, int val) {
		for(int i=0; i<9; i++) {
			if(map[row][i] == val) {
				return false;
			}
		}
		for(int i=0; i<9; i++) {
			if(map[i][col] == val) {
				return false;
			}
		}
		
		//3X3 크기 상자 안에도 중복 확인
		int st_row = (row/3) * 3;
		int st_col = (col/3) * 3;
		
		for(int i=st_row; i<st_row+3; i++) {
			for(int j=st_col; j<st_col+3; j++) {
				if(map[i][j] == val) {
					return false;
				}
			}
		}
		return true;
	}

	//실행 메인 
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner kb= new Scanner(System.in);
		map = new int[9][9];
		
		for(int i=0; i<9; i++) {
			for(int j=0; j<9; j++) {
				map[i][j] = kb.nextInt();
			}
		}	
		DFS(0, 0);
	}
}
728x90