문제
https://www.acmicpc.net/problem/2580
2580번: 스도쿠
스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루
www.acmicpc.net
문제 요약
빈 칸이 0으로 주어진 9 × 9 스도쿠를 모두 채워 출력한다.
규칙
1. 각각의 가로줄과 세로줄에는 1 부터 9까지의 숫자가 한 번씩만 나타나야 한다.
2. 굵은 선으로 구분되어 있는 3 × 3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
코드
#include <iostream>
using namespace std;
int board[9][9]; // 스도쿠판
// 0 ~ 8번 가로, 세로, 사각형 존재한다고 가정
bool row[9][10]; // row[i][j]: i번째 가로줄에 j라는 숫자 존재
bool col[9][10]; // col[i][j]: i번째 세로줄에 j라는 숫자 존재
bool square[9][10]; // square[i][j]: i번째 사각형에 j라는 숫자 존재
// (x, y)에 해당하는 좌표는 (x / 3) * 3 + (y / 3)번째 사각형에 해당
// (사각형의 번호가 세로는 3씩 증가, 가로는 1씩 증가하므로)
// 스도쿠 판 출력
void printBoard() {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cout << board[i][j] << " ";
}
cout << "\n";
}
}
void dfs(int cnt) {
// cnt는 지금까지 차례대로 스도쿠 판을 채운 수의 개수 (탐색 횟수 = 깊이)
int x = cnt / 9; // 현재 채울 수의 x좌표
int y = cnt % 9; // 현재 채울 수의 x좌표
// 스도쿠 판이 모두 채워졌다면 출력
if (cnt == 81) {
printBoard();
exit(0); // 종료
}
// 해당 칸이 비어있으면
if (board[x][y] == 0) {
// 1부터 9까지 되는 숫자 탐색
for (int i = 1; i <= 9; i++) {
if (!row[x][i] && !col[y][i] && !square[(x / 3) * 3 + (y / 3)][i]) {
// 해당 칸에 i가 들어갔을 경우 체크
row[x][i] = true;
col[y][i] = true;
square[(x / 3) * 3 + (y / 3)][i] = true;
board[x][y] = i;
dfs(cnt + 1);
// 결국 판을 다 채우지 못하고 재귀를 빠져나왔다면, 다시 복구
row[x][i] = false;
col[y][i] = false;
square[(x / 3) * 3 + (y / 3)][i] = false;
board[x][y] = 0;
}
}
}
// 해당 칸에 숫자가 이미 있다면
else {
dfs(cnt + 1); // 다음 숫자 채우기
}
}
int main() {
// cin, cout 시간 최적화
ios::sync_with_stdio(false);
cin.tie(NULL); cout.tie(NULL);
// 스도쿠 판 입력
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cin >> board[i][j];
// 이미 채워진 숫자 체크
if (board[i][j] != 0) {
row[i][board[i][j]] = true;
col[j][board[i][j]] = true;
square[(i / 3) * 3 + (j / 3)][board[i][j]] = true;
}
}
}
dfs(0);
return 0;
}
코드 설명
재귀를 이용한 완전 탐색 (백트래킹)으로 문제를 해결한다.
먼저 가로줄, 세로줄, 사각형 영역에 숫자 해당 숫자가 존재하는지 여부를 체크하는 2차원 배열 3개로 관리한다.
+ 초기 값은 입력을 받으면서 체크한다.
dfs 함수는 현재까지 재귀로 호출된 횟수를 인자(cnt)로 받고, 이는 (0, 0)부터 몇 번째 수인지를 나타낸다.
cnt를 통해 현재 채워야 하는 스도쿠 판의 좌표를 알 수 있고, 이를 통해 그 좌표가 몇 번째 줄 인지, 몇 번째 사각형 영역에 속해 있는지를 찾을 수 있다.
cnt가 81, 즉 스도쿠의 (7, 7) 마지막 인덱스에 해당한다면 스도쿠 판이 모두 채워졌다는 의미이므로 스도쿠 판을 출력하고, 프로그램을 종료한다.(프로그램을 종료하지 않으면, 만족하는 모든 경우의 수를 출력하므로 주의해야한다.)
스도쿠 판 내 사각형 영역의 번호를 아래처럼 가정했을 때,
0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 |
0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 |
0 | 0 | 0 | 1 | 1 | 1 | 2 | 2 | 2 |
3 | 3 | 3 | 4 | 4 | 4 | 5 | 5 | 5 |
3 | 3 | 3 | 4 | 4 | 4 | 5 | 5 | 5 |
3 | 3 | 3 | 4 | 4 | 4 | 5 | 5 | 5 |
6 | 6 | 6 | 7 | 7 | 7 | 8 | 8 | 8 |
6 | 6 | 6 | 7 | 7 | 7 | 8 | 8 | 8 |
6 | 6 | 6 | 7 | 7 | 7 | 8 | 8 | 8 |
사각형 영역 번호가 세로는 3씩 증가, 가로는 1씩 증가하므로, (x, y)에 해당하는 좌표는 (x / 3) * 3 + (y / 3)번째 사각형에 해당한다.
dfs를 진행하면서, 해당 좌표에 0 ~ 9 중에 가능한 숫자를 모두 넣어보고, 아니라면 (프로그램이 종료되지 않았다면) 넣은 숫자가 틀렸다는 것이므로 다시 숫자를 지워야 한다.
'Algorithm Problems > 완전 탐색' 카테고리의 다른 글
[백준/C++] 18428번: 감시 피하기 (0) | 2024.03.17 |
---|---|
[백준/C++] 28421번: 곱하기와 쿼리 (2) | 2024.02.23 |
[백준/Python] 3085번: 사탕 게임 (0) | 2023.09.27 |
[백준/Python] 1018번: 체스판 다시 칠하기 (2) | 2023.09.26 |
[백준/Python] 15970번: 화살표 그리기 (0) | 2023.09.25 |