백준 2239 - 스도쿠
3 minute read
문제링크
문제설명
- 9개의 3x3 크기의 보드에 1부터 9까지의 숫자가 중복 없이 나타나도록 보드를 채우는 스도쿠 게임
- 각 행에 1부터 9까지의 숫자가 중복 없이 나오고, 각 열에 1부터 9까지의 숫자가 중복 없이 나오고, 각 3x3 사각형에 1부터 9까지의 숫자가 중복 없이 나와야 한다.
문제접근
- 숫자를 채워야 하는 빈칸의 좌표들을
vector<pair<int, int>> node
에 담아둔다.
- 백트래킹을 적용하기 위해서 가장 편리한 방법이다.
- 노드로 관리하지 않고 좌표로 접근하면서 백트래킹을 구현했었는데 매번 빈칸을 찾아줘야 한다는 점은 시간을 많이 소요하게 되고, 로직적으로 문제가 발생해서 문제해결이 쉽지 않았다.
vector<char> cand[x][y]
배열에는 현재 빈칸에 들어갈 수 있는 숫자 후보들을 담아둔다.
- 1부터 9까지 차례대로 모든 숫자를 넣어볼 수는 있지만, 처음부터 걸러낼 수 있는 숫자들은 제거하는 것이 좋다고 판단했다/
- 정답이 여러 개인 경우 81자리의 숫자가 가장 작은 경우를 정답으로 출력한다는 의미는 작은 숫자들을 먼저 넣어서 스도쿠를 완성하도록 하라는 의미이다. 따라서, 가장 처음에 완성된 스도쿠를 정답으로 출력하고
exit(0)
을 호출하여 프로그램을 종료한다.
return
으로 재귀함수들을 모두 빠져나오도록 구현할 수 있지만, 시간초과에 걸렸다.
구현
#include <iostream>
#include <string>
#include <vector>
#include <memory.h>
#include <cctype>
#include <queue>
using namespace std;
vector<string> board, answer;
vector<char> cand[9][9];
vector<pair<int, int>> node;
void setCand(int x, int y) {
int digit[10];
memset(digit, 0, sizeof(digit));
// check row
for (int c = 0; c < 9; c++) {
if (c == y) continue;
digit[board[x][c] - '0'] = 1;
}
// check col
for (int r = 0; r < 9; r++) {
if (r == x) continue;
digit[board[r][y] - '0'] = 1;
}
// check box
int sx = (x / 3) * 3;
int sy = (y / 3) * 3;
for (int dx = 0; dx < 3; dx++) {
for (int dy = 0; dy < 3; dy++) {
if ((sx + dx == x) && (sy + dy == y)) continue;
digit[board[sx + dx][sy + dy] - '0'] = 1;
}
}
// set cand
for (int i = 1; i <= 9; i++) {
if (digit[i] == 0) cand[x][y].push_back(i+'0');
}
}
void printAnswer() {
for (auto ele : answer) {
cout << ele << "\n";
}
}
bool checkRow(int x, int y, char ele) {
for (int c = 0; c < 9; c++) {
if (c == y) continue;
if (ele == board[x][c]) return true;
}
return false;
}
bool checkCol(int x, int y, char ele) {
for (int r = 0; r < 9; r++) {
if (r == x) continue;
if (ele == board[r][y]) return true;
}
return false;
}
bool checkBox(int x, int y, char ele) {
int sx = (x / 3) * 3;
int sy = (y / 3) * 3;
for (int dx = 0; dx < 3; dx++) {
for (int dy = 0; dy < 3; dy++) {
if ((sx + dx == x) && (sy + dy == y)) continue;
if (board[sx + dx][sy + dy] == ele) return true;
}
}
return false;
}
void playSdoku(int v) {
if (v == node.size()) {
answer = board;
printAnswer();
exit(0);
}
int xpos = node[v].first;
int ypos = node[v].second;
for (auto ele : cand[xpos][ypos]) {
if (checkRow(xpos, ypos, ele) || checkCol(xpos, ypos, ele) || checkBox(xpos, ypos, ele)) continue;
board[xpos][ypos] = ele;
playSdoku(v + 1);
board[xpos][ypos] = '0';
}
}
int main()
{
cin.tie(NULL);
ios_base::sync_with_stdio(false);
for (int i = 0; i < 9; i++) {
string str;
cin >> str;
board.push_back(str);
}
// 빈칸과 후보 숫자 정하기
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
if (board[r][c] == '0') {
setCand(r, c);
}
}
}
// set node
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
if (cand[r][c].size() == 0) continue;
node.push_back(make_pair(r, c));
}
}
playSdoku(0);
printAnswer();
return 0;
}
Leave a comment