[백준/C++] 2580번 : 스도쿠 (백트래킹 연습)
스도쿠
문제
스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 일부 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.
나머지 빈 칸을 채우는 방식은 다음과 같다.
- 각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
- 굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.
또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.
이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.
게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.
입력
아홉 줄에 걸쳐 한 줄에 9개씩 게임 시작 전 스도쿠판 각 줄에 쓰여 있는 숫자가 한 칸씩 띄워서 차례로 주어진다. 스도쿠 판의 빈 칸의 경우에는 0이 주어진다. 스도쿠 판을 규칙대로 채울 수 없는 경우의 입력은 주어지지 않는다.
출력
모든 빈 칸이 채워진 스도쿠 판의 최종 모습을 아홉 줄에 걸쳐 한 줄에 9개씩 한 칸씩 띄워서 출력한다.
스도쿠 판을 채우는 방법이 여럿인 경우는 그 중 하나만을 출력한다.
제한
- 12095번 문제에 있는 소스로 풀 수 있는 입력만 주어진다.
- C++14: 80ms
- Java: 292ms
- PyPy3: 1172ms
예제 입력 1
0 3 5 4 6 9 2 7 8
7 8 2 1 0 5 6 0 9
0 6 0 2 7 8 1 3 5
3 2 1 0 4 6 8 9 7
8 0 4 9 1 3 5 0 6
5 9 6 8 2 0 4 1 3
9 1 7 6 5 2 0 8 0
6 0 3 7 0 1 9 5 2
2 5 8 3 9 4 7 6 0
예제 출력 1
1 3 5 4 6 9 2 7 8
7 8 2 1 3 5 6 4 9
4 6 9 2 7 8 1 3 5
3 2 1 5 4 6 8 9 7
8 7 4 9 1 3 5 2 6
5 9 6 8 2 7 4 1 3
9 1 7 6 5 2 3 8 4
6 4 3 7 8 1 9 5 2
2 5 8 3 9 4 7 6 1
해당 문제는 스도쿠 보드를 입력받고, 0으로 입력된 빈칸에 스도쿠 룰에 맞는 숫자들을 채워나가는 문제이다.
#include <iostream>
#include <string>
#define SIZE 9
using namespace std;
int puzzle[10][10];
int row[10][10];
int col[10][10];
int square[10][10];
int SquareNum(int x, int y)
{
return (x / 3) * 3 + (y / 3);
}
bool DFS(int num)
{
if(num == SIZE*SIZE)
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
cout << puzzle[i][j] << ' ';
cout << '\n';
}
return true;
}
int x = num / SIZE;
int y = num % SIZE;
if (puzzle[x][y] != 0)
return DFS(num + 1);
else
{
for(int i = 1; i <= SIZE; i++)
{
if(!row[x][i] && !col[y][i] && !square[SquareNum(x,y)][i])
{
row[x][i] = true;
col[y][i] = true;
square[SquareNum(x, y)][i] = true;
puzzle[x][y] = i;
if (DFS(num + 1))
return true;
row[x][i] = false;
col[y][i] = false;
square[SquareNum(x, y)][i] = false;
puzzle[x][y] = 0;
}
}
}
return false;
}
int main()
{
ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
cin >> puzzle[i][j];
if (puzzle[i][j] != 0)
{
row[i][puzzle[i][j]] = true;
col[j][puzzle[i][j]] = true;
square[SquareNum(i,j)][puzzle[i][j]] = true;
}
}
}
DFS(0);
return 0;
}
소스코드 풀이과정을 정리해보자.
1. 스도쿠 보드를 입력받을 2차원 배열 puzzle[10][10]과, 스도쿠 규칙에 따라 가로, 세로, 3*3 사각형 범위에서 사용된 숫자들을 판별하기 위한 row, col, square 배열을 선언하였다.
2. 보드 숫자를 입력받고 0이 아닌 경우, row, col, square 배열을 true로 설정하였다.
(만약 하나의 열이 1.2,0,4,5,6,7,8,9 라면, false(0),true(1),true(2),false(3),true,,,true(9) 가 저장된다)
3. square 배열을 초기화 할때는 스도쿠의 3*3 사각형 범위 중 어느 위치에 사각형인지를 첫번째 배열 원소로 저장하기 때문에 SquareNum 함수를 통해 인덱스를 찾았다.
(x / 3 ) * 3 + (y / 3) => x: 7, y : 4 라고 한다면, 2*3 + 1 = 7 번째 사각형이 나온다.
4. 0번째 인덱스부터 puzzle 배열 원소를 검사하기 시작해서 DFS 매개변수 num이 81이 되어 puzzle의 끝까지 검사를 완료하면 프로그램이 종료된다.
5. 0번째 인덱스부터 검사하여 원소 값이 0인 경우, 1부터 9까지 차례로 숫자를 넣어보며 row(행), col(열), square(사각형) 배열이 전부 false로 각 규칙에 사용되지 않은 숫자를 찾는다.
(0이 아닌 경우엔 DFS(num+1) 으로 바로 다음 인덱스로 넘어간다)
6. 찾은 숫자를 puzzle 배열에 저장하며 다음 puzzle 인덱스를 검사하기 위해 DFS(num+1) 을 호출해 다음 검사로 넘어간다.
7. 이때 DFS 함수의 반환값 bool이 true인 경우 계속해서 다음 DFS(num+1)로 넘어가며 검사하도록 하고, false인 경우에는 백트래킹 원리를 적용하여 초기화했던 배열과 원소값을 전부 원상태로 돌려놓은 후 DFS의 결과로 false를 반환한다.
8. num이 81까지 도달한 경우 puzzle 배열을 전부 출력하고 true를 반환하여 DFS 함수를 끝낸다.
https://www.acmicpc.net/problem/2580
2580번: 스도쿠
스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루
www.acmicpc.net