https://www.acmicpc.net/problem/2580
2580번: 스도쿠
스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루
www.acmicpc.net
nQueen문제를 이해했다면 충분히 풀 수 있는 문제다.
쉽게 풀진 못했고 2가지 케이스 때문에 많은 고생을 했다.
접근 아이디어
배열을 입력받을 때 0인 배열의 위치 값을 list에 넣는다. 그 후 백트래킹을 하면서 하나하나 값을 확인한다.
해당 자리에 가능한 숫자 배열을 만들고 그 숫자를 하나하나 넣어가며 백트래킹을 한다.
다른 분 코드를 보니 배열을 만들지 않고 숫자를 넣어서 체크하던데 그 방식이 공간 복잡도는 훨씬 줄일 수 있겠다.
static boolean[] getPossibleNum(int y, int x) {
boolean[] possibleNum = new boolean[10];
//작은 배열에서 넣을 수 있는 숫자 계산
for (int i = (y/3) * 3; i < (y/3) * 3 + 3 ; i++) {
for (int j = (x/3) * 3; j < (x/3) * 3 + 3; j++) {
possibleNum[map[i][j]] = true;
}
}
//큰 배열에서 넣을 수 있는 값 계산
for (int i = 0; i < 9; i++) {
possibleNum[map[i][x]] = true;
possibleNum[map[y][i]] = true;
}
//해당자리의 n이 가질 수 있는 값 리스트가 리턴됨
return possibleNum;
}
해당 자리에 2,3,4가 들어간다면 2,3,4 배열만 false다.
그리고 1부터 10까지 해당 숫자를 다 넣어보면서 재귀를 수행한다.
for (int i = 1; i < 10; i++) {
if(!possibleNum[i]) {
map[p.y][p.x] = i;
sdoku(n+1);
}
}
잘 한 부분
해당 자리에 넣을 수 있는 숫자를 계산하는 부분을 어렵지 않게 작성했고. 예제 케이스는 쉽게 통과했다.
nQueen문제를 이해하고 풀어서 백트래킹 설계를 잘했다.
부족한 부분
백트래킹을 수행하고 실패했다면 0으로 만들어줘야 한다. 이 부분을 놓쳐서 많은 시간을 소비했다.
0으로 만드는 코드를 넣는 곳을 선별하는 것도 쉽지 않았다. 막연히 if(!possibleNum[i[) else {이곳}
에다 넣으면 해결되는 줄 알았는데 틀렸다. for문 바깥에 0으로 만드는 코드를 삽입해서 해결했지만 아직 그 차이를 이해하지 못했다.
두 번째는 시스템 종료 구문이다. 한 번만 출력해야 하니 한번 출력하고 시스템을 종료해야 한다. 나는 스택에 쌓인 함수를 한 번에 종료할 수 있는 방법이 있는 줄 알고 한참을 고민했는데 그냥 시스템을 종료해야 했다. 조금 황당했고 정답률 이 왜 낮은지 알 수 있었다.
세 번째는 공간 복잡도다. 다른 분의 코드보다 조금 빠르지만 공간은 4배나 잡아먹는다. 아마 list를 선언하는 부분과 가능한 숫자를 boolean배열로 받아오는 부분에서 문제가 발생한 것 같다.
또한 문제를 쪼개서 가로, 세로, 작은 네모 이렇게 비교해가며 해결할 수 있었는데 한 번에 모든 숫자를 찾아서 해결하려고 했다. 문제를 쪼개는 습관을 길러야 한다!
package backjon;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
public class Main {
static int T;
static int[][] map;
static List<Place> list = new ArrayList<>();
public static void main(String[] args) throws Exception {
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());
for (int j = 0; j < 9; j++) {
int n = Integer.parseInt(st.nextToken());
map[i][j] = n;
if(n == 0) list.add(new Place(i, j));
}
}
sdoku(0);
}
private static void sdoku(int n) {
if(n == list.size()) {
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;
}
Place p = list.get(n);
boolean[] possibleNum = getPossibleNum(p.y, p.x);
for (int i = 1; i < 10; i++) {
if(!possibleNum[i]) {
map[p.y][p.x] = i;
sdoku(n+1);
}
}
map[p.y][p.x] = 0;
}
static boolean[] getPossibleNum(int y, int x) {
boolean[] possibleNum = new boolean[10];
//작은 배열에서 넣을 수 있는 숫자 계산
for (int i = (y/3) * 3; i < (y/3) * 3 + 3 ; i++) {
for (int j = (x/3) * 3; j < (x/3) * 3 + 3; j++) {
possibleNum[map[i][j]] = true;
}
}
//큰 배열에서 넣을 수 있는 값 계산
for (int i = 0; i < 9; i++) {
possibleNum[map[i][x]] = true;
possibleNum[map[y][i]] = true;
}
//해당자리의 n이 가질 수 있는 값 리스트가 리턴됨
return possibleNum;
}
static class Place{
int y, x;
public Place(int y, int x) {
this.y = y;
this.x = x;
}
}
}
'알고리즘' 카테고리의 다른 글
백준 14889 자바 스타트와 링크 (0) | 2022.03.22 |
---|---|
백준 14888 자바 연산자 끼워넣기 (0) | 2022.03.22 |
백준 9663 N-Queen 자바 (0) | 2022.03.17 |
프로그래머스 주차요금계산 자바 (0) | 2022.03.16 |
백준 2470 자바 드래곤 커브 (0) | 2022.03.09 |