본문 바로가기
Algorithm/백준

백준 1248 맞춰봐 - C++

by 강깅꽁 2020. 7. 23.
반응형

음 문제가 엄청 길다.

필요한 내용이 중간 중간 있을까봐 전부 꼼꼼히 읽었지만 마지막 부분만 읽으면 된다..

규현이는 -10~10까지 알고 있기 때문에 총  21가지 수를 쓸 수 있다(0포함)

만약에 규현이가

A= [-2, 5, -3, 1]이라는 수를 말한다면 이것을 2차원 배열 S에 다음과 같이 표현할 수 있다.

  0 1 2 3
0 - + 0 +
1 X + + +
2 X X - -
3 X X X +

S[i][j]는 A[i]부터 A[j]까지의 합이 양수면 +, 음수면 -, 0이면 0을 나타낸다.

여기서 i는 j보다 작거나 같아야 한다.

i가0이고 j가 1이라 해보자 A[0]+A[1]이 되고 이것의 합은 3이기 때문에 S[0][1]에 +가 저장된다.

i가 0이고 j가 2이라면 A[0]+A[1]+A[2]가 되고 이 합은 0이기 때문에 S[0][2]에 0이 저장된다.

S[1][0]의 경우 i(1)가 j(0)보다 크기 때문에 X가 들어간다.

 

접근

문제에서 A 배열을 주고 S 이차원 배열을 만드는 것이 아니라

S 이차원 배열을 주고 A 배열을 만들어야 한다. 즉 역으로 구해야 한다.

브루트 포스로 풀게 되면 N의 크기 만큼 A 배열에 -10~10까지 21가지 수를 랜덤하게 넣어서 풀어야 하는데

N은 10보다 작거나 같으니 최대 21의 10제곱의 경우의 수가 나온다.  

ex. A= [21가지 경우의 수, 21가지 경우의 수 ..... ,21가지 경우의 수]->21^10

 

 

S 이차원 배열을 보면 i와 j가 같을때 A[i]의 수가 어떤 범위에 속할지 알 수 있다.

주어진 S가 위와 같다고 했을 때 S[0][0]은 -이므로 A[0]은 0~10까지의 수는 올 수가 없다.

또 S[1][1]은 +이므로 -10~0까지의 수는 올 수가 없다.

이런 식으로 각 A[i]의 범위를 한정지을 수 있다.

하지만 이렇게 각 수의 범위를 한정 지어도 최악의 경우 각 범위는 10일 테니  10의 N제곱 만큼의 경우의 수가 나온다. 

N의 최대 값은 10이니 10의 10제곱도 여전히 경우의 수가 크다.

 

따라서 백 트래킹을 통해 더이상 의미 없는 조건은 실행시키지 않도록 한다.

여기서 의미 없는 조건은 A배열을 만들다 보면 S[i][j]에 해당하지 않는 수가 있는데 이런 경우 A배열에 수를 추가하는 것이 아니라 추가 된 수를 변경해주어야 한다. 

ex. A에 -4, 2가 있다고 했을 때 이 수는 S [0][1]의 조건과 맞지 않다. 

 

그래서 다음과 같이 구현해 보았다.

isThisRight에서 현재까지 만들어진 A배열을 통해 S[i][j]에 들어갈 값을 만드는데 이때 실제 S[i][j]값과 일치하는지 확인한다. 일치하지 않는게 있다면 A 배열에 입력된 수가 잘못되었기 때문에 입력된 수를 다시 넣어준다.

하지만 isThisRight함수에서 비교하는 함수의 호출도 빈번하고 시간도 오래 걸려서 시간초과가 뜬다.

#include <iostream>
#include <string>

using namespace std;
char S[10][10];
int N, A[10];
bool isThisRight(int idx){
    for(int low=0; low<idx; low++){
        int sum=A[low];
        char state;
        for(int col=low+1; col<idx; col++){
            sum+=A[col];
            if(sum > 0) state = '+';
            else if(sum < 0) state = '-';
            else state ='0';
            
            if(state != S[low][col]) return false;
        }
    }
    return true;
}
bool guessMe(int idx){
    if(idx == N){
        if( !isThisRight(idx) ) return false;
        
        for(int k=0; k<N; k++){
            cout << A[k] << ' ';
        }
        cout << '\n';
        return true;
    }
    
    char curState = S[idx][idx];
    bool flag;
    if(curState == '+'){
        for(int i=1; i<=10; i++){
            A[idx] = i;
            flag = guessMe(idx+1);
            if(flag) return true;
        }
    }
    else if(curState == '-'){
        for(int i=-1; i>=-10; i--){
            A[idx] = i;
            flag = guessMe(idx+1);
            if(flag) return true;
        }
    }
    else {
        A[idx] = 0;
        flag = guessMe(idx+1);
        if(flag) return true;
    }
    
}

int main()
{
    int strIdx=0;
    string str;
    cin >> N >> str;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            if(i>j){
              S[i][j] = 'X';
              continue;
            }  
            S[i][j] = str[strIdx++];
        }
    }
    
    guessMe(0);
 

    return 0;
}

 

 

그래서 isThisRight함수를 S 이차원 배열 전부를 검사하는 것이 아니라 필요한 부분만 검사하도록 수정하였다.

#include <iostream>
#include <string>

using namespace std;
char S[10][10];
int N, A[10];
bool isThisRight(int idx){
    int sum = 0;
    for(int low=idx; low>=0; low--){
        sum+=A[low];
        char state;
        if(sum > 0) state = '+';
        else if(sum < 0) state = '-';
        else state ='0';
            
        if(state != S[low][idx]) return false;
        
    }
    return true;
}
bool guessMe(int idx){
    if(idx == N) return true;
    
    char curState = S[idx][idx];
    if(curState == '+'){
        for(int i=1; i<=10; i++){
            A[idx] = i;
            if( isThisRight(idx) && guessMe(idx+1) ) return true;
            
        }
    }
    else if(curState == '-'){
        for(int i=-1; i>=-10; i--){
            A[idx] = i;
            if( isThisRight(idx) && guessMe(idx+1) ) return true;
        }
    }
    else {
        A[idx] = 0;
        return guessMe(idx+1);
    }
    return false;
    
}

int main()
{
    int strIdx=0;
    string str;
    cin >> N >> str;
    for(int i=0; i<N; i++){
        for(int j=0; j<N; j++){
            if(i>j){
              S[i][j] = 'X';
              continue;
            }  
            S[i][j] = str[strIdx++];
        }
    }
    guessMe(0);
    for(int i=0; i<N; i++){
        cout << A[i] << ' ';
    }
    return 0;
}