알고리즘/백준

[C++] 백준 7696 : 반복하지 않는 수

0802ojw 2024. 1. 23. 20:04
#include <iostream>
#include <string>
# define MAX 8

using namespace std;

int cnt = 0; // 몇번째 수열(수) 인지 기록할 변수
int arr[10]; //수열을 기록할
int answer [1000001][10]; //재귀로 구한 데이터를 넣을 곳
bool used[10]; //해당 숫자를 사용했는지 기록(visited와 유사)

bool nonZeroUsed() {
    for (int i = 0; i < 10; i++) {
        if (i && used[i]) {
            return true;
        }
    }
    return false;
}

void initialize() {
    cnt = 0;
    fill(begin(arr), end(arr), 0);
    fill(begin(used), end(used), false);
}

void dfs(int m) {
    if (cnt == 1000001) {
        return;
    }

    if (m == MAX) {
        for (int i = 0; i < MAX; i++) {
            answer[cnt][i] = arr[i];
        }
        cnt++;
        return;
    }

    for (int i = 0; i < 10; i++) {
        //used를 검사해서  visited에서 0 이외에 다른것을 썼는지 검사한다
        bool isNonZeroUsed = nonZeroUsed();
        
        // 001 002 는 되는데 400은 안되게 used를 변형시킨다.
        if (!isNonZeroUsed) {
            used[0] = false;
        }

        if (!used[i]) { 
            used[i] = true;
            //used를 또 검사해서  used에서 0 이외에 다른것을 썼는지 검사한다
            isNonZeroUsed = nonZeroUsed();
            //0이외의 것이 쓰이기전의 0과 이후의 0을 구별하자.
            if (!isNonZeroUsed) {
                arr[m] = -1;
            }
            else {
                arr[m] = i;
            }

            dfs(m + 1);
            used[i] = false;
        }
    }
}


// 메인 함수
int main() {
    int input;
    dfs(0);
    while (true) {
        // 숫자 입력
        cin >> input;

        // 입력된 숫자가 0이면 반복 종료
        if (input == 0) {
            break;
        }
        else {
            // 변수 및 배열 초기화 함수
            initialize();
            // 조건을 만족하는 수를 찾아서 출력
            for (int i = 0; i < MAX; i++) {
                if (answer[input][i] != -1) {
                    cout << answer[input][i];
                }
            }
            cout << '\n';
        }

    }
}

 

dfs (재귀) 로 먼저 1000000번째 까지의 반복하지 않는수에 관한 데이터를 이차원배열에 입력하고 시작했다.

 

매 입력때마다 이러한 연산을 반복하는건 시간초과를 초래한다.


매 테스트 케이스에서 변수 초기화에서 실수를 했다.

배열은 fill로 초기화를 진행하였다.

개인적으로 0을 처리하는게 이문제에서 가장 어려웠다

반복되지 않는 숫자는 used라는 boolean형 배열을 사용하면 쉽게 제거할 수 있지만,

0과 같은경우는. 001 과 100이 서로 다른 케이스다.

100은 세어지면 안되는것이고, 001은 0을 지워야한다.

전자의 경우는 used를 조작할 필요성이 있었고

후자의 경우는 arr에 -1을 마킹할 필요가 있었다.

이것을 해결해주는게 isNonZeroUsed 변수와 nonZeroUsed 함수이다.

지금 까지 방문한 숫자(노드) 에서 0만 있었는지, 혹은 다른 숫자가 있었는지 여부에 따라서,

그 0을 처리하는 방법이 달라야 하기 때문이다.

개인적으로 isNonZeroUsed가 이 문제의 핵심이지만 이게 제일 효율적인 방법인지는 모르겠다.

추가적으로 이 코드를 더 효율적으로 하기 위해선 

1000000번째 숫자까지 처음부터 구하고 시작하기보다는 

지금까지 나온 가장 큰 x번째를 저장하고, x번째보다 큰 입력이 나온다면 거기서부터 다시 재귀 호출을 시작하는게 더 효율적일 것이다.

그러나 내 생각으론 재귀호출을 멈췄다가 다시 진행하는건 arr와 used와 그리고 재귀 호출의 depth 까지 기억해야되서 (내 생각이 틀릴수도 있다) 이 문제가 요구하는것 이상의 구현일것같아서 (사실 귀찮아서) 그만 두었다.