https://www.acmicpc.net/problem/2239
2239번: 스도쿠
스도쿠는 매우 간단한 숫자 퍼즐이다. 9×9 크기의 보드가 있을 때, 각 행과 각 열, 그리고 9개의 3×3 크기의 보드에 1부터 9까지의 숫자가 중복 없이 나타나도록 보드를 채우면 된다. 예를 들어 다
www.acmicpc.net
코드설명
DFS(깊이우선탐색) + 브루트포스(bruteforce) + 조합(Combination) 을 활용하는 문제입니다.
문제로직은 2가지로 나뉩니다.
- simulate 함수는 백트래킹을 이용해 스도쿠를 풀어나가는 함수입니다. 이 함수는 현재 위치 (r, c)에서 가능한 숫자를 시도하고, 가능한 경우 재귀적으로 다음 칸으로 진행합니다.
- 만약 현재 위치에 이미 숫자가 채워져 있다면 해당 숫자를 놓고 다음 칸으로 이동합니다.
- 현재 위치에 숫자가 채워져 있지 않다면 1부터 9까지의 숫자를 시도하면서 가능한 경우에 대해 재귀 호출을 합니다.
- availableCheck 함수는 현재 위치에 특정 숫자를 놓을 수 있는지 여부를 검사하는 함수입니다. 행, 열, 그리고 작은 3x3 격자를 검사하여 중복된 숫자가 없는지 확인합니다.
- 행과 열을 검사하여 중복된 숫자가 있는지 확인합니다.
- 3x3 작은 격자를 검사하여 중복된 숫자가 있는지 확인합니다. 격자의 시작 위치를 (r / 3) * 3 ~ (r / 3) * 3 + 3, (c / 3) * 3 ~ (c / 3) * 3 + 3로 계산하여 해당 부분을 검사합니다.
문제에서 현재 위치에 특정숫자를 둘때, 행과 열을 확인하는 부분은 쉽지만, 3x3 으로 나누어서 처리하는 부분에서 놓쳤었던 점이 있습니다.
아래에서 i < newR * 3 + 3 에서 처음에는 단순히 i < newR + 3 으로 하여 3x3 사각형에서 올바르게 스도쿠를 두지 못해서 틀렸습니다.
i의 범위를 올바르게 i = newR * 3; i < newR * 3 + 3 으로 처리해야만 합니다.
//시작칸의 3x3 을 구하기 위해.
int newR = r / 3;
int newC = c / 3;
for(int i=newR * 3; i < newR * 3 + 3; i++) {
for(int j=newC * 3; j < newC * 3 + 3; j++) {
if(map[i][j] == num) {
return false;
}
}
}
제출 시 틀린다면, 아래의 입력으로 테스트해보는 것을 추천드립니다.
입력
000000000
000000000
000000000
000000000
000000000
000000000
000000000
000000000
000000000
출력
123456789
456789123
789123456
214365897
365897214
897214365
531642978
642978531
978531642
코드
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Stack;
import java.util.StringTokenizer;
public class Main {
public static int N, M, K, H;
public static int[][] map = new int[9][9];
public static int idx = 0;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < 9; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
String str = st.nextToken();
for (int j = 0; j < 9; j++) {
map[i][j] = str.charAt(j) - '0';
}
}
simulate(0, 0, 0);
}
public static void simulate(int level, int r, int c) {
if( c >= 9 ) {
r = r + 1;
c = 0;
}
if(level >= 9 * 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);
return ;
}
if(r >= 9) return ;
if(map[r][c] == 0) {
for(int i=1;i<10;i++) {
map[r][c] = i;
if(availableCheck(r, c, map[r][c]) == true) {
simulate(level + 1, r, c + 1);
}
map[r][c] = 0;
}
}else { //0이 아닐경우에만 아무것도 안하고 넘어가야합니다. 처음에 무조건 넘어가도록 하여 0이 아닌경우가 가장 먼저 나왔습니다. 이러한 점들을 유의합니다.
simulate(level + 1, r, c + 1); // 아예 선택하지 않는경우에도 계속됩니다.
}
}
public static boolean availableCheck(int r, int c, int targetNum) {
//행 검사.
for(int i=0;i<9;i++) {
if(i == r && c == c ) continue;
if(map[i][c] == targetNum) return false;
}
//열 검사
for(int i=0;i<9;i++) {
if(r == r && i == c ) continue;
if(map[r][i] == targetNum) return false;
}
//해당 칸을 검사.
//9개의 칸으로 생각할 수 있습니다.
// ( 0 ~ (r / 3) * 3 , 0 ~ ( c / 3) * 3) { (r/3) ~ ( r / 3 ) , 3 ~ (c/3) * 3) ( 0 ~ ( r / 3 ), 6 ~ ( c / 3 ) * 3}
// ( 1 ~ (r / 3) * 3 , 0 ~ ( c / 3) * 3 )
//
for(int i=(r / 3) * 3; i<(r / 3) * 3 + 3; i++) {
for(int j=( c / 3 ) * 3; j < ( c/ 3 ) * 3 + 3; j++) {
if(r == i && c == j) continue;
if(map[i][j] == targetNum) {
return false;
}
}
}
return true;
}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
public static int N, M;
public static int answer = 0;
public static int[][] map;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
map = new int[9][9];
for(int i=0;i<9;i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
String inputLine = st.nextToken();
for(int j=0;j<9;j++) {
map[i][j] = inputLine.charAt(j) - '0';
}
}
simulate(0, 0, 0);
}
public static void simulate(int level, int r, int c) {
if( c >= 9 ) {
r = r + 1;
c = 0;
}
if(level >= 9*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);
return ;
}
if( r >= 9) {
return ;
}
//만약 0 이라면,
if(map[r][c] == 0) {
for(int num = 1; num <= 9; num++) {
//행, 열, 3x3 에 해당 숫자가 존재하지 않을경우에만 넣어야할 것 입니다.
if ( checkSudoku(num, r, c) == true ) {
map[r][c] = num;
simulate(level + 1, r, c + 1);
}
}
map[r][c] = 0;
}
//만약 0 이 아닐경우에만 담은칸으로 그냥 넘어가도록 해야합니다.
else if(map[r][c] != 0) {
//아무것도 하지 않고 넘어가는 경우입니다.
simulate(level + 1, r, c + 1);
}
}
public static boolean checkSudoku(int num, int r, int c) {
for(int col = 0; col < 9; col++) {
if(map[r][col] == num) {
return false;
}
}
for(int row = 0; row < 9; row++) {
if(map[row][c] == num) {
return false;
}
}
//시작칸의 3x3 을 구하기 위해.
int newR = r / 3;
int newC = c / 3;
for(int i=newR * 3; i < newR * 3 + 3; i++) {
for(int j=newC * 3; j < newC * 3 + 3; j++) {
if(map[i][j] == num) {
return false;
}
}
}
return true;
}
}