[백준 - 골드] 2580. 스도쿠

김도은·2025년 1월 19일

알고리즘-자바

목록 보기
7/19

이번 주의 문제

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

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

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

각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.

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

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

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

내가 푼 방식

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main {

	static int[][] sdoku;

	public static void main(String[] args) throws IOException {

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		sdoku = new int[9][9];

		for (int r = 0; r < 9; r++) {
			StringTokenizer st = new StringTokenizer(br.readLine());

			for (int c = 0; c < 9; c++) {
				sdoku[r][c] = Integer.parseInt(st.nextToken());
			}
		} // 입력

		start(0, 0); // 스도쿠 풀기

		StringBuilder sb = new StringBuilder();

		for (int r = 0; r < 9; r++) {
			for (int c = 0; c < 9; c++) {
				sb.append(sdoku[r][c]).append(" ");
			}
			sb.append("\n");
		}

		System.out.println(sb);
	}

	private static boolean start(int R, int C) {

		if (C == 9) {
			// 열이 끝나면
			return start(R+1, 0); // 다음줄로 다시 시작
		}

		if (R == 9)
			return true;
		
		if(sdoku[R][C] != 0) return start(R, C+1);

		for (int i = 1; i <= 9; i++) {
			if (check(R, C, i)) {
				sdoku[R][C] = i;
				
				if(start(R, C+1)) {
					return true;
				};
				
				sdoku[R][C] = 0;
			}
		}
		
		return false;
	}

	private static boolean check(int R, int C, int num) {

		// 가로 검사
		for (int c = 0; c < 9; c++) {
			if (sdoku[R][c] == num) {
				return false;
			}
		}

		// 세로 검사
		for (int r = 0; r < 9; r++) {
			if (sdoku[r][C] == num) {
				return false;
			}
		}

		// 정사각 검사
		int nR = R / 3 * 3;
		int nC = C / 3 * 3;

		for (int r = nR; r < nR + 3; r++) {
			for (int c = nC; c < nC + 3; c++) {
				if (sdoku[r][c] == num) {
					return false;
				}
			}
		}

		return true;
	}
}

함께 보면 좋은 알고리즘

백트래킹

현재 상태에서 다음 상태로 가는 모든 경우의 수를 찾아 모든 경우의 수가 유망하지 않다고 판단되면, 이전 상태로 돌아간다. 탐색할 필요가 없는 상태를 제외하는 것을 가지치기라고 부른다.

해당 알고리즘을 잘 사용하면 최적화를 잘 할 수 있다. 현재 스도쿠 문제를 정말 스도쿠 풀듯이 풀게 되면, 시간 초과가 발생하기 때문에, 조건문을 잘 사용하여 가지치기를 효율적으로 해주면 좋다.

private static boolean start(int R, int C) {

		if (C == 9) {
			// 열이 끝나면
			return start(R+1, 0); // 다음줄로 다시 시작
		}

		if (R == 9)
			return true;
		
		if(sdoku[R][C] != 0) return start(R, C+1);

		for (int i = 1; i <= 9; i++) {
			if (check(R, C, i)) {
				sdoku[R][C] = i;
				
				if(start(R, C+1)) {
					return true;
				};
				
				sdoku[R][C] = 0;
			}
		}
		
		return false;
	}

해당 풀이에서 if(start(R,C+1)) return true; 해당 부분이 없다면, 모든 경우의 수를 다 돌고 나오는 것이다.

profile
프론트엔드와 디자인

0개의 댓글