Uknow's Lab.
article thumbnail

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

 

2239번: 스도쿠

스도쿠는 매우 간단한 숫자 퍼즐이다. 9×9 크기의 보드가 있을 때, 각 행과 각 열, 그리고 9개의 3×3 크기의 보드에 1부터 9까지의 숫자가 중복 없이 나타나도록 보드를 채우면 된다. 예를 들어 다

www.acmicpc.net

 

난이도 : 골드 4
태그 : 구현, 백트래킹

 

 

1. 설명

유명한 보드게임인 스도쿠를 기반으로 한 코테 문제입니다.

빈칸에 들어갈 수 있는 경우의 수를 하나씩 찾아 대입해보고,

최종적으로 결과값을 도출해내는 백트래킹 문제입니다.

DFS를 사용하여 풀이하였습니다.

 

 

2. 소스코드

입력값 세팅

<kotlin />
import java.io.BufferedReader import java.io.InputStreamReader import kotlin.system.exitProcess var map = Array(9) { Array(9) { 0 } } data class Point(var x: Int, var y: Int) val zeroPointArray = ArrayList<Point>() var emptyCount = 0 fun main() { val br = BufferedReader(InputStreamReader(System.`in`)) repeat(9) { x -> val line = br.readLine() repeat(9) { y -> map[x][y] = line[y].digitToInt() if (map[x][y] == 0) { zeroPointArray.add(Point(x, y)) } } } emptyCount = zeroPointArray.size dfs(0) }

 

Point는 좌표값 x, y를 담을 data class 이며,

map은 스도쿠의 보드 (9x9) 정보를 담을 배열입니다.

emptyCount는 빈칸의 개수, zeroPointArray는 빈 칸의 좌표를 담을 Point형 ArrayList입니다.

 

값을 받으면서, 빈칸(0)이면 하나씩 zeroPointArray에 추가합니다.

 

 

 

후보값 도출

<kotlin />
fun findKeys(x: Int, y: Int): ArrayList<Int> { val nums = Array(10) { false } for (i in 0 until 9) { nums[map[x][i]] = true } for (i in 0 until 9) { nums[map[i][y]] = true } val squareX = when (x) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } val squareY = when (y) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } for (i in 0 until 3) { for (j in 0 until 3) { if (i == x && j == y) continue nums[map[squareX + i][squareY + j]] = true } } val result = ArrayList<Int>() for (i in 1..9) { if (!nums[i]) result.add(i) } return result }

 

x, y좌표가 주어지면 해당 좌표에 들어갈 수 있는 숫자를 return 해주는 메소드 입니다.

 

nums라는 boolean 배열을 사용하여 한 번 이상 등장한 숫자를 체크해주었습니다. (0은 버리고 1~9까지 사용)

 

 

 

 

 

<kotlin />
for (i in 0 until 9) { nums[map[x][i]] = true } for (i in 0 until 9) { nums[map[i][y]] = true }

 

가로와 세로에 있는 값들을 탐색하며 한 번 이상 등장한 숫자를 true로 바꿔줍니다.

 

 

 

 

 

<kotlin />
val squareX = when (x) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } val squareY = when (y) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } for (i in 0 until 3) { for (j in 0 until 3) { if (i == x && j == y) continue nums[map[squareX + i][squareY + j]] = true } }

 

해당 좌표가 포함된 3x3블럭을 탐색하는 부분입니다.

 

 

 

<code />
val result = ArrayList<Int>() for (i in 1..9) { if (!nums[i]) result.add(i) } return result

최종적으로, nums 배열을 체크하며 값이 false인 값을 찾아 ArrayList에 담아 return 합니다.

 

 

 

백트래킹

<kotlin />
fun dfs(depth: Int) { if (depth == emptyCount) { map.forEach { it -> it.forEach { it2 -> print(it2) } println() } exitProcess(0) } val point = zeroPointArray[depth] val foundKey = findKeys(point.x, point.y) for (i in 0 until foundKey.size) { map[point.x][point.y] = foundKey[i] dfs(depth + 1) map[point.x][point.y] = 0 } }

 

depth는 깊이, 즉 현재 채워진 빈칸의 개수를 의미합니다.

 

채워진 빈칸의 개수가 전체 빈칸의 개수와 같을 경우, map의 좌표를 출력 후

exitProcess(0) { 자바의 System.exit(0)과 같습니다)를 통해 프로그램을 종료시킵니다.

 

 

현재 깊이에 맞는 빈칸의 좌표와 findKeys 메소드를 사용하여,

해당 좌표에 들어갈 수 있는 후보값들을 도출합니다.

 

해당 값을 하나씩 대입하면서, dfs를 재귀적으로 호출합니다.

dfs탐색이 끝나면 해당 좌표를 다시 0으로 돌립니다.

 

 

스도쿠 퍼즐의 특성 상, 복수의 정답이 나올 수 있는데,

문제의 지문으로는 사전순으로 빠른 것을 출력하도록 요구하였습니다.

 

위 코드에서는 findKeys 메소드에서 애초에 사전순으로 후보값들을 전달해주기 때문에,

가장 처음 완성된 맵이 정답입니다.

 

 

 

전체 소스코드

<kotlin />
import java.io.BufferedReader import java.io.InputStreamReader import kotlin.system.exitProcess var map = Array(9) { Array(9) { 0 } } data class Point(var x: Int, var y: Int) val zeroPointArray = ArrayList<Point>() var emptyCount = 0 fun main() { val br = BufferedReader(InputStreamReader(System.`in`)) repeat(9) { x -> val line = br.readLine() repeat(9) { y -> map[x][y] = line[y].digitToInt() if (map[x][y] == 0) { zeroPointArray.add(Point(x, y)) } } } emptyCount = zeroPointArray.size dfs(0) } fun dfs(depth: Int) { if (depth == emptyCount) { map.forEach { it -> it.forEach { it2 -> print(it2) } println() } exitProcess(0) } val point = zeroPointArray[depth] val foundKey = findKeys(point.x, point.y) for (i in 0 until foundKey.size) { map[point.x][point.y] = foundKey[i] dfs(depth + 1) map[point.x][point.y] = 0 } } fun findKeys(x: Int, y: Int): ArrayList<Int> { val nums = Array(10) { false } for (i in 0 until 9) { nums[map[x][i]] = true } for (i in 0 until 9) { nums[map[i][y]] = true } val squareX = when (x) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } val squareY = when (y) { 0, 1, 2 -> 0 3, 4, 5 -> 3 6, 7, 8 -> 6 else -> -1 } for (i in 0 until 3) { for (j in 0 until 3) { if (i == x && j == y) continue nums[map[squareX + i][squareY + j]] = true } } val result = ArrayList<Int>() for (i in 1..9) { if (!nums[i]) result.add(i) } return result }

 

 

 

 

3. 후기

평소 스도쿠 퍼즐을 주로 하는데,

코딩테스트 문제로 이렇게 접하니 반가운 문제였네요.

profile

Uknow's Lab.

@유노 Uknow

인생은 Byte와 Double 사이 Char다. 아무말이나 해봤습니다.