문제

부등호 백준 2529번

리뷰

부등호의 개수k와 부등호 리스트가 주어진다.

< < < > < < > < >

k+1 개의 숫자를 나열하여 부등호를 만족시켜야 한다. 0부터 9까지의 숫자만 사용 가능하다.

이를 만족하는 수열 예시 2개는 아래와 같다. 수열은 모두 달라야 한다. 숫자를 모두 이어붙이면 하나의 '숫자'가 된다.

//  3456128790 뿐만 아니라 5689023174도 아래와 같이 부등호 관계 A를 만족시킨다. 
3 < 4 < 5 < 6 > 1 < 2 < 8 > 7 < 9 > 0
5 < 6 < 8 < 9 > 0 < 2 < 3 > 1 < 7 > 4

주어진 부등호 관계를 만족하는 k+1자리의 최소 정수와 최대 정수를 출력해야 한다.

0부터 9까지 10가지의 숫자를 중복하지 않고 수열을 만드는 것은 재귀를 이용하여 N과 M 시리즈 문제에서 해봤다.

아래는 기본 구조다. index는 채워야 할 자리의 index 이다.

만약 4자리 숫자로 구성된 수열을 만들어야 하면, (n == 4 라고 가정)

index가 0,1,2,3 까지 변화하면서 for문에서 숫자를 선택하여 채우고, ( 이 때, ch[i] 배열을 검사하면서 선택이 이미 된 )

index가 4가 된 순간에 if문 조건을 만족시키면서 수열이 완성되서 return 된다.

bool ch[11]; // 중복을 피하기 위한 체크 배열 

void Combination(int index){ 

    if(index == n ){ // 수열 완성. 
        //
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

         ch[i] = true; // 선택
         Combination(index+1); // 다음 자리수 선택을 위해 재귀 호출 
         ch[i] = false; // 선택 안함  
    } 
}

이 문제에서는 백트레킹 기법을 쓴다.

백트레킹은 의미 없는 함수 호출을 피하기 위해 앞에 조건문으로 검사해서 함수 호출 횟수를 줄이는 것이다.

k 개의 부등호가 주어지면, 수열의 숫자는 k+1개로 구성된다.

Combination 함수는 index 자리의 숫자를 선택하면서 숫자를 string num에 담아서 넘긴다.

만약 첫째 자리 숫자를 선택해야 하는 index == 0 인 상황에는, 무조건 숫자를 선택한다. 재귀 호출을 피할 이유가 없다.

이와 달리 index가 1 이상인 경우, 즉 둘째, 셋째 그 이상의 자리의 숫자를 선택해야 하는 상황일 때는 앞자리 숫자와 부등호를 고려해야 한다.

// 왜냐하면 첫째 숫자가 9 이고 부등호가 < 인 경우가 있다. 그런데 9 를 초과하는 1자리 숫자는 없다.

if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1]))

// 따라서 앞자리 숫자, 현재 선택하려는 숫자, 앞자리 부등호를 인자로 넘겨서 검사한다.
// 아래는 함수의 시그니처다.     
bool SignCheck(char a, char b, char oper); 

SignCheck 함수는 현재 선택하려는 숫자 b가 부등호를 만족시킬 수 있는 숫자라면 true를 반환한다.

수열이 완성되면 index가 k+1이 되는 순간이다. 그 때 ans 벡터에 넣는다.

답은 최대, 최소 숫자니까 ans 벡터를 정렬해서 맨앞, 맨뒤 값을 출력하면 된다.

void Combination(int index, string num){ 
    // index : 선택해야 할 (수열에서의) 숫자 자리. 0부터 9까지. 

    if(index == (k+1) ){ // 수열 완성. 벡터에 넣는다 
        ans.push_back(num); 
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

        // 첫번째 자리 숫자를 고르는 거라면, 무조건 선택. (index == 0)
        //  그게 아니라면, 앞자리 숫자 num[index-1] 과 현재 선택하려는 숫자 i 와 부등호를 넘긴다.  
        if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1])){
            ch[i] = true; // 선택
             Combination(index+1, num + to_string(i)); // 다음 자리수 선택을 위해 재귀 호출 
            ch[i] = false; // 선택 안함  
        }

    } 
}

코드

#include <iostream>
#include <algorithm> 
#include <vector>
#include <string>
using namespace std;


int k;
char sign[11];  // 부등호 담은 수열   
int num[11];     // 조합으로 만들어진 수열  
bool ch[11];  // 숫자 중복 불허 
vector<string> ans; // 부등호를 만족하는 수열을 저장하는 벡터 
string maxnum, minnum;

bool SignCheck(char a, char b, char oper){ // 부등호를 만족시키는지 검사

    if(oper == '<'){
        if(a>b) return false;

    } else {// '>'
        if(a<b) return false;
    }
    return true;
}

void Combination(int index, string num){ 
    // index : 선택해야 할 (수열에서의) 숫자 자리. 0부터 9까지. 

    if(index == (k+1) ){ // 수열 완성. 벡터에 넣는다 
        ans.push_back(num); 
        return;      
    }

    for(int i = 0; i <= 9; i++){
        if(ch[i]) continue; // 이미 사용한 숫자라면 지나감

        // 첫번째 자리 숫자를 고르는 거라면, 무조건 선택. (index == 0)
        //  그게 아니라면, 앞자리 숫자 num[index-1] 과 현재 선택하려는 숫자 i 와 부등호를 넘긴다.  
        if(index == 0 || SignCheck(num[index-1], i+'0', sign[index-1])){
            ch[i] = true; // 선택
             Combination(index+1, num + to_string(i)); // 다음 자리수 선택을 위해 재귀 호출 
            ch[i] = false; // 선택 안함  
        }
    } 
}


int main(void){

    cin >> k;

     for(int i = 0; i < k; i++){
         cin >> sign[i];
    } // 입력받기 끝 

    Combination(0, "");    

    sort(ans.begin(), ans.end()); // 오름차순 정렬 후, 맨 뒤가 최대, 맨 앞이 최소

    cout << ans[ans.size()-1] << '\n' << ans[0];

    return 0;
} 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

N과 M(5), (6) 백준 15654번 c++  (0) 2020.10.22
알고스팟 1261번 백준 c++  (0) 2020.10.22
부분수열의 합 백준 1182번  (0) 2020.10.20
외판원순회2 백준 10971번 c++  (0) 2020.10.20
N과 M (3),(4)  (0) 2020.10.20

+ Recent posts