[02580] 스도쿠

Byeongmin·2021년 7월 5일
0
post-thumbnail

[02580] 스도쿠

문제

스도쿠는 18세기 스위스 수학자가 만든 '라틴 사각형'이랑 퍼즐에서 유래한 것으로 현재 많은 인기를 누리고 있다. 이 게임은 아래 그림과 같이 가로, 세로 각각 9개씩 총 81개의 작은 칸으로 이루어진 정사각형 판 위에서 이뤄지는데, 게임 시작 전 일부 칸에는 1부터 9까지의 숫자 중 하나가 쓰여 있다.

스도쿠

나머지 빈 칸을 채우는 방식은 다음과 같다.

각각의 가로줄과 세로줄에는 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
굵은 선으로 구분되어 있는 3x3 정사각형 안에도 1부터 9까지의 숫자가 한 번씩만 나타나야 한다.
위의 예의 경우, 첫째 줄에는 1을 제외한 나머지 2부터 9까지의 숫자들이 이미 나타나 있으므로 첫째 줄 빈칸에는 1이 들어가야 한다.

가로

또한 위쪽 가운데 위치한 3x3 정사각형의 경우에는 3을 제외한 나머지 숫자들이 이미 쓰여있으므로 가운데 빈 칸에는 3이 들어가야 한다.

정사각형

이와 같이 빈 칸을 차례로 채워 가면 다음과 같은 최종 결과를 얻을 수 있다.

결과

게임 시작 전 스도쿠 판에 쓰여 있는 숫자들의 정보가 주어질 때 모든 빈 칸이 채워진 최종 모습을 출력하는 프로그램을 작성하시오.

입력

아홉 줄에 걸쳐 한 줄에 9개씩 게임 시작 전 스도쿠판 각 줄에 쓰여 있는 숫자가 한 칸씩 띄워서 차례로 주어진다. 스도쿠 판의 빈 칸의 경우에는 0이 주어진다. 스도쿠 판을 규칙대로 채울 수 없는 경우의 입력은 주어지지 않는다.

출력

모든 빈 칸이 채워진 스도쿠 판의 최종 모습을 아홉 줄에 걸쳐 한 줄에 9개씩 한 칸씩 띄워서 출력한다.

스도쿠 판을 채우는 방법이 여럿인 경우는 그 중 하나만을 출력한다.

제한

baekjoon의 백트래킹 알고리즘으로 풀 수 있는 입력만 주어진다. 다음은 그 알고리즘의 수행 시간이다.
C++14: 80ms
Java: 292ms
PyPy3: 1172ms

코드


#include <iostream>

using namespace std;

/* 조건 */

/* 변수 */
int board[9][9];

/* 함수 */
void printBoard();
bool isAvailable(int x, int y, int n) {
    for(int i = 0; i < 9; i++) {
        if(board[x][i] == n) return false; // 가로 확인
        if(board[i][y] == n) return false; // 세로 확인
        for(int a = 0; a < 3; a++) // 정사각형 확인
            for(int b = 0; b < 3; b++)
                if(board[((x/3)*3) + a][((y/3)*3) + b] == n) return false;
    }
    return true;
}

void solve(int x, int y) {
    if(board[x][y] == 0) { // 빈칸인 경우
        for(int i = 1; i <= 9; i++) { // 1부터 9까지 넣어봄
            if(isAvailable(x, y, i)) { // 넣어도 되는 경우
                board[x][y] = i; // 숫자를 넣어봄
                if(x == 8 && y == 8) printBoard(); // 마지막 칸까지 넣은 경우 출력후 종료
                solve(x + ((y+1) / 9), (y+1) % 9); // 다음 칸 확인
                board[x][y] = 0;
            }
        }
    } else { // 빈칸이 아닌 경우
        if(x == 8 && y == 8) printBoard(); // 마지막에 도달한 경우 출력 후 종료
        solve(x + ((y+1) / 9), (y+1) % 9); // 다음 칸 확인
    }
}

void printBoard() { // 전체 출력 후 종료하는 함수
    for(int i = 0; i < 9; i++) {
        for(int j = 0; j < 9; j++) {
            cout << board[i][j] << ' ';
        }
        cout << '\n';
    }
    exit(0);
}

int main() {
    /* Fast cin cout */
    ios_base :: 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]; // 세로가 x축 가로가 y축
        }
    }

    /* 풀이 */
    solve(0, 0); // (0, 0)에서 시작

    /* 출력 */
    /* solve()에서 끝까지 채운 경우
     * printBoard() 실행하여 출력됨 */
}

풀이

함수

  • void printBoard();
    스도쿠를 모두 채운 후 모두 출력하고 종료하는 함수

  • bool isAvailable(int x, int y, int n)
    (x, y)에 숫자 n이 들어갈 수 있는지 확인하는 함수.
    가로, 세로, 정사각형에 자신과 같은 숫자가 있으면 false를 출력한다.
    정사각형을 확인하는 과정에서 (x/3)3(x / 3) * 3은 n이 포함된 정사각형의 왼쪽 위를 나타낸다.

  • void solve(int x, int y)
    백트래킹의 적용을 위해 사용되는 재귀함수와 형식의 함수이다.
    (x, y)부터 한칸씩 나아가면서 확인하는데,
    비어있는 경우 1부터 9까지 직접 넣어보는 방식으로 진행된다.
    채우던 중 가능한 숫자가 없는 경우 돌아가 다시 빈칸으로 만들며 돌아간다.
    마지막 board[8][8]을 채우거나 이미 채워져 있던 경우 printBoard() 함수를 실행하며 프로그램이 종료된다.

부가 설명

solve함수에 설명을 다 해버렸다...

출처 : https://www.acmicpc.net/problem/2580

profile
Handong Global Univ.

0개의 댓글