문제
스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 일부 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.
나머지 빈 칸을 채우는 방식은 다음과 같다.
- 각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
- 굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.
또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.
이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.
게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.
입력
아홉 줄에 걸쳐 한 줄에 9개씩 게임 시작 전 스도쿠판 각 줄에 쓰여 있는 숫자가 한 칸씩 띄워서 차례로 주어진다. 스도쿠 판의 빈 칸의 경우에는 0이 주어진다. 스도쿠 판을 규칙대로 채울 수 없는 경우의 입력은 주어지지 않는다.
출력
모든 빈 칸이 채워진 스도쿠 판의 최종 모습을 아홉 줄에 걸쳐 한 줄에 9개씩 한 칸씩 띄워서 출력한다.
스도쿠 판을 채우는 방법이 여럿인 경우는 그 중 하나만을 출력한다.
문제 풀이
1. 0이 위치한 행,열 위치정보를 어레이에 넣는다.
- 빈칸이 문제에서는 0으로 주어지고 있다.
- 우리가 채워야 할 칸은 0이기 때문에 0이 있는 좌표만 가져온 다음 해당 좌표에서 행, 열, 3x3 에 위치한 수와 겹치지 않는 수를 넣으면 된다.
코드는 아래와 같다. ( 값을 받으면서 동시에 값이 0이 경우에는 ary에 집어 넣고 있다.)
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int temp = sc.nextInt();
info[i][j] = temp;
if (temp == 0) {
ary.add(new int[] {i,j});
}
}
}
2. 같은 행에 있는 원소 중에 겹치는 수가 있는지를 검사한다.
static boolean backtrackingRow(int h, int w, int target) {
for (int i = 0; i < n; i++) {
if (info[h][i] == target) {
return false;
}
}
return true;
}
3. 같은 열에 있는 원소 중에 겹치는 수가 있는지를 검사한다.
- 사실 행과 열을 확인하는 작업은 하나의 for문으로 백트래킹이 가능하지만 메소드를 두개로 나누는 것이 의도를 파악하기 더 좋아보여서 아래와 같이 코드를 구현하였다.
static boolean backtrackingCol(int h, int w, int target) {
for (int i = 0; i < n; i++) {
if (info[i][w] == target) {
return false;
}
}
return true;
}
4. 해당 위치에 속한 3×3 칸에 중복되는 수가 있는지를 검사한다.
- 메소드 이름을 뭐라고 지었으면 좀 더 직관적이었을까...
static boolean backTrackingSquare(int h, int w, int target) {
for (int i = h; i <= h + 2; i++) {
for (int j = w; j <= w + 2; j++) {
if (info[i][j] == target) {
return false;
}
}
}
return true;
}
5. 모든 빈칸을 채웠다면 결과를 출력하고, 종료하자.
- count의 수가 ary에 들어있는 원소의 수와 같다는 것은 모든 0을 다 채웠다는 의미다.
- 결과값을 출력해주고 종료하자
if (count == ary.size()) {
// 결과 출력
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(info[i][j]+" ");
}
System.out.println();
}
System.exit(0);
}
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static int n = 9;
static int[][] info = new int[n][n];
static ArrayList<int[]> ary = new ArrayList<>();
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int temp = sc.nextInt();
info[i][j] = temp;
if (temp == 0) {
ary.add(new int[] {i,j});
}
}
}
sudoku(0);
}
static void sudoku(int count) {
if (count == ary.size()) {
// 결과 출력
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(info[i][j]+" ");
}
System.out.println();
}
System.exit(0);
}
int[] location = ary.get(count);
int h = location[0];
int w = location[1];
for (int i = 1; i <= n; i++) {
int squareH = (h / 3) * 3;
int squareW = (w / 3) * 3;
if (backTrackingSquare(squareH, squareW, i) && backtrackingCol(h, w, i) && backtrackingRow(h, w, i)) {
info[h][w] = i;
sudoku(count+1);
}
}
info[h][w]=0;
}
static boolean backtrackingRow(int h, int w, int target) {
for (int i = 0; i < n; i++) {
if (info[h][i] == target) {
return false;
}
}
return true;
}
static boolean backtrackingCol(int h, int w, int target) {
for (int i = 0; i < n; i++) {
if (info[i][w] == target) {
return false;
}
}
return true;
}
static boolean backTrackingSquare(int h, int w, int target) {
for (int i = h; i <= h + 2; i++) {
for (int j = w; j <= w + 2; j++) {
if (info[i][j] == target) {
return false;
}
}
}
return true;
}
}
'알고리즘 > 순열' 카테고리의 다른 글
백준 1987번: 알파벳 (0) | 2021.07.30 |
---|---|
[JAVA] 백준 9663번: N-Queen (0) | 2021.07.26 |
[JAVA] 백준 14889번: 스타트와 링크 (0) | 2021.07.21 |
[JAVA] 백준 14888번: 연산자 끼워넣기 (0) | 2021.07.21 |
[JAVA] 백준 1339번: 단어수학 (0) | 2021.07.20 |