티스토리 뷰

Computer Science/BOJ

BOJ 1422. 숫자의 신

yeongminb 2024. 10. 8. 23:07

 

문제 링크

 

문제 풀이

 이 문제는 $K \leq 50$개의 자연수 중에서 $N\space\space(K \leq N \leq 50)$개의 자연수를 중복을 허용하고 뽑아 이어붙였을 때 만들어질 수 있는 가장 큰 수를 출력하는 문제이다. 이 때 주어진 K개의 자연수 모두 적어도 한 번씩은 사용되어야한다. 

 

 이 문제를 접근할 때 두 가지를 고려하였다. 첫 번째는 주어지는 각 숫자의 사용 개수이고, 두 번째는 숫자들의 사용 순서이다. 먼저 각 숫자의 사용 개수는 당연히 자릿수가 가장 많은 숫자를 많이 사용하는 것이 유리하다. 왜냐하면 자릿수가 많을수록 숫자가 커지기 때문이다. 그래서 자릿수가 가장 큰 숫자를 $N-K$번 사용하고 나머지를 1번씩만 사용할 생각을 하였다. 만약 자릿수가 가장 큰 숫자가 2개 이상이라면 더 큰 숫자를 사용하는 것이 좋다는 것도 자명하다.

 

 두 번째는 사용 순서인데 조금만 생각해보면 맨앞자리가 큰 수 일 수록 먼저 사용하는 것이 좋다는 것을 알 수 있다. 예를 들어 22 9 8 5가 있으면 98522와 같이 앞의 자리가 큰 숫자들을 앞쪽에 배치하는 것이 좋다. 그런데 까다로운 것은 맨 앞자리수가 같은 수들이다. 여기에는 두 가지 경우가 있다.

 

Case 1) 다른 자릿수가 존재하는 경우 $(ex. 3151, 31842)$

  예시처럼 뒤에 있는 숫자들이 차이가 나면 최초로 더 큰 수가 나온 수가 더 좋다. 이 경우에서는 5와 8을 비교했을 때 31842를 사용하는 것이 3151을 사용하는 것보다 유리하다.

 

Case 2) 다른 자릿수가 존재하지 않는 경우 $(ex. 7811, 78)$

   이런 경우에서는 78까지 모두 똑같기 때문에 Case1처럼 비교할 수 없다. 그런데 직관적으로 생각해보면 781178으로 쓰는 것보다 787811로 쓰는 것이 더 크다. 즉 11과 78을 비교했을 때 78이 더 크기 때문에 이 경우에선 78을 먼저 쓰는 것이 좋다.

 

이를 수식적으로 정리해서 적어보자. 먼저 함수 $f(N_1,N_2)$을 $N_1$이 먼저 오는 게 유리할 때 1 true, $N_2$가 먼저 오는게 유리할 때 0 false를 반환하는 함수로 정의하자.$$let) f(N_1,N_2) := \begin{cases}true & when\space N_1\space comes\space first\\false & when\space N_2\space comes\space first\end{cases}$$

 

추가적으로 $N_1$과 $N_2$의 자릿수 개수를 각각 $l_1,\space l_2$로 정의하자.

$$let) l_1 :=(number\space of\space digits\space in\space N_1)$$$$let) l_2 :=(number\space of\space digits\space in\space N_2)$$

 

일반성을 잃지 않고 $l_1 \geq l_2$라고 가정하고, $i$라는 인덱스를 $N_1[i] \neq N_2[i],\space\space i<l_2$을 만족하도록 하는 최초의 인덱스라고 하자. 그러면 함수 $f$는 다음과 같은 값을 가진다.$$f(N_1,N_2) = \begin{cases}N_1[i] > N_2[i] &if \space\space\space^\exists i\\f(N_1[l_2]...N_1[l_1-1],N_2) & if\space\space\space ^\nexists i \end{cases}$$

 

 이를 바탕으로 코드를 짜면 된다. 여기서 $f$라는 함수를 기준으로 주어진 $K$개의 숫자들을 정렬하고 순서대로 출력하면된다. 특히 자릿수가 가장 많은 숫자는 $N-K$번 출력해줘야한다.

 

 

코드

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

int K,N,mxLength = 0;
vector<string> leadingDigit[10];
string longestNum;

bool compareNum(string num1, string num2){
    int l1 = num1.length();
    int l2 = num2.length();
    if(num1 == num2) return true;
    if(l1 == l2){
        for(int i=0;i<l1;i++){
            if(num1[i] > num2[i]) return true;
            if(num1[i] < num2[i]) return false;
        }
    } else if(l1 > l2){
        for(int i=0;i<l2;i++){
            if(num1[i] > num2[i]) return true;
            if(num1[i] < num2[i]) return false;
        }
        return compareNum(num1.substr(l2), num2);
    } else return !compareNum(num2,num1);
}

void input(){
    cin>>K>>N;
    for(int i=0;i<K;i++){
        string num;
        cin>>num;
        leadingDigit[num[0] - '0'].push_back(num);
        if(mxLength < num.length()){
            mxLength = num.length();
            longestNum = num;
        } else if(mxLength == num.length()){
            if(compareNum(num, longestNum))longestNum = num;
        }
    }
}

void solve(){
    for(int i=1;i<=9;i++){
        if(leadingDigit[i].empty()) continue;
        sort(leadingDigit[i].begin(),leadingDigit[i].end(),compareNum);
    }
    bool flag = true;
    for(int i=9;i>=1;i--){
        if(leadingDigit[i].empty()) continue;
        for(int j=0;j<leadingDigit[i].size();j++){
            if(flag && leadingDigit[i][j] == longestNum){
                for(int k=0;k<=N-K;k++) cout<<longestNum;
                flag = false;
            } else {
                cout<<leadingDigit[i][j];
            }
        }
    }
}

int main(){
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    
    input();
    solve();
}

'Computer Science > BOJ' 카테고리의 다른 글

BOJ 13141. Ignition  (3) 2024.10.30
BOJ 32074. XOR 최대  (1) 2024.10.26
BOJ 14956. Philosopher's Walk  (2) 2024.09.18
BOJ 1019. 책 페이지  (2) 2024.08.31
BOJ 15486. 퇴사 2  (2) 2024.08.27
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함