[C++] 백준(BOJ) - 3687 : 성냥개비

인프런에 있는 큰돌님의 강의 10주완성 C++ 코딩테스트 | 알고리즘 코딩테스트를 듣고 정리한 필기입니다.

문제

BOJ - 3687 : 성냥개비(링크)

성냥개비는 숫자를 나타내기에 아주 이상적인 도구이다. 보통 십진수를 성냥개비로 표현하는 방법은 다음과 같다.

성냥개비의 개수가 주어졌을 때, 성냥개비를 모두 사용해서 만들 수 있는 가장 작은 수와 큰 수를 찾는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수가 주어진다. 테스트 케이스는 최대 100개 이다. 각 테스트 케이스는 한 줄로 이루어져 있고, 성냥개비의 개수 n이 주어진다. (2 ≤ n ≤ 100)

출력

각 테스트 케이스에 대해서 입력으로 주어진 성냥개비를 모두 사용해서 만들 수 있는 가장 작은 수와 가장 큰 수를 출력한다. 두 숫자는 모두 양수이어야 하고, 숫자는 0으로 시작할 수 없다.

알고리즘

  1. DP
  2. 그리디

풀이

  • 문제 범위
    • 1 < n < 101
    • 성냥개비로 1을 붙여나갈 때(2개 소모) 50자리 수가 나온다
    • int형 10자리수가 최대
    • long long형 20자리수가 최대
    • string 기반으로 문제풀이
  • 최대값
    • 그리디
      • 자리수가 최대이면 최대값이다
      • n의 개수 짝수 : 1로만 자리수를 채울 수 있다 (2개 소모)
      • n의 개수 홀수 : 7(3개 소모) + 1로만 채우기 (최대 자리수)
  • 최소값
    • 모든 경우의 수 찾아야 함
    • DP 활용
    • Botton-up
      • dp배열을 0에서부터 만들어 나가며 최소값을 string dp배열에 갱신
      • 큰 자리수부터 최소값을 넣고, 끝자리에 최소값을 더해가는 방법
    • Top-down
      • 우선 0~9까지를 붙이기
      • 남은 성냥(here - a[i])을 넘겨가며 최소값을 만들어내기
    • 예외처리
      • 맨 앞자리에 0이들어가는 경우의 수 빼주기

코드 1 : Bottom-Up

// BOJ - 3678 : 성냥개비
#include<bits/stdc++.h>
using namespace std;
const int INF = 2147000000;

int t, n;
int a[] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
string dp[104], max_str = "111111111111111111111111111111111111111111111111119";

string get_min_str(string a, string b)
{
    if (a.size() == b.size()) return (a < b ? a : b); // 스트링의 비교는 선 사이즈 체크 필수
    if (a.size() < b.size()) return a;
    return b;
}

string findMax(int here)
{
    string res = "";
    if (here & 1) // here이 홀수인 경우 7 붙이기
    {
        res += '7';
        here -= 3;
    }
    while (here)
    {
        res += '1';
        here -= 2;
    }
    return res;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> t;

    fill(dp, dp + 104, max_str);

    // 1. 바텀업 방식
    dp[0] = "";
    for (int i = 2; i < 104; i++)
    {
        for (int j = 0; j <= 9; j++)
        {
            if (i - a[j] < 0) continue; // 남은 성냥개비 수보다 사용하려는 수가 드는 성냥개비가 적으면 x
            if (j == 0 && dp[i - a[j]] == "") continue; // 성냥개비가 6개가 남았고 맨앞의 인덱스에 0이 들어가는 경우이면
            dp[i] = get_min_str(dp[i], dp[i - a[j]] + to_string(j));
        }
    }

    while (t--)
    {
        cin >> n;
        cout << dp[n] << " " << findMax(n) << '\n';
    }
    return 0;
}

코드 2 : Top-Down

// BOJ - 3678 : 성냥개비
#include<bits/stdc++.h>
using namespace std;
const int INF = 2147000000;

int t, n;
int a[] = { 6, 2, 5, 5, 4, 5, 6, 3, 7, 6 };
string dp[104], max_str = "111111111111111111111111111111111111111111111111119";

string get_min_str(string a, string b)
{
    if (a.size() == b.size()) return (a < b ? a : b); // 스트링의 비교는 선 사이즈 체크 필수
    if (a.size() < b.size()) return a;
    return b;
}

// 2. 탑다운 방식
string findMin(int here)
{
    if (here == 0) return "";
    string& res = dp[here];
    if (res != max_str) return res;
    for (int i = 0; i <= 9; i++)
    {
        if (here - a[i] < 0) continue;
        if (here == n && i == 0) continue; // 성냥개비 모두 썼을때, 첫번째 인덱스가 0인 경우 불가능
        res = get_min_str(res, to_string(i) + findMin(here - a[i]));
    }
    return res;
}

string findMax(int here)
{
    string res = "";
    if (here & 1) // here이 홀수인 경우 7 붙이기
    {
        res += '7';
        here -= 3;
    }
    while (here)
    {
        res += '1';
        here -= 2;
    }
    return res;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> t;

    while (t--)
    {
        cin >> n;
    	fill(dp, dp + 104, max_str);
        cout << findMin(n) << " " << findMax(n) << '\n';
    }
    return 0;
}

평가

  • 한문제에서 그리디와 DP를 어떨 때 사용해야하는지 파악하고 풀이할 수 있다
  • Bottom-Up과 Top-Down 두 방식으로 DP를 풀어내고, string 형의 dp값을 리턴하여 대소비교를 하는 것까지 총망라된 좋은 문제

© 2023 Jinsoo Lee. All rights reserved.

Powered by Hydejack v9.1.6