Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 2293 - 동전 1

sm_amoled 2021. 12. 25. 13:01

문제링크

https://www.acmicpc.net/problem/2293

문제

n가지 종류의 동전이 있다. 각각의 동전이 나타내는 가치는 다르다. 이 동전을 적당히 사용해서, 그 가치의 합이 k원이 되도록 하고 싶다. 그 경우의 수를 구하시오. 각각의 동전은 몇 개라도 사용할 수 있다.

사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다.

입력

첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 경우의 수를 출력한다. 경우의 수는 2^31보다 작다.

조건

  • 시간 제한 : 0.5s
  • 메모리 제한 : 4MB

해설

https://sihyungyou.github.io/baekjoon-2293/ 를 참고하였습니다! ㅠㅠㅠ

K=8일때 동전의 값들이 1/2/5라고 해보자.

1로 만들 수 있는 경우의 수
     1 2 3 4 5 6 7 8
dp : 1 1 1 1 1 1 1 1

1, 2로 만들 수 있는 경우의 수
     1 2 3 4 5 6 7 8
dp : 1 2 2 3 3 4 4 5

1, 2, 5로 만들 수 있는 경우의 수 
     1 2 3 4 5 6 7 8
dp : 1 2 2 3 4 5 6 7

위 값들을 만드는 데에는 다음의 식을 따른다. dp[i] = dp[i] + dp[i-n] 1, 2, 5를 이용해 7을 만드는 경우를 가정해보면, 7을 만드는 경우의 수 = 1, 2를 이용해 7을 만드는 경우의 수 + 5를 추가하여 7을 만드는 경우의 수 ( = 2를 만드는 경우의 수 )와 같다. 따라서, 5를 추가한다면 dp[7] 의 값에 dp[7] + dp[7-5]이 대입된다.

풀이

입력받은 동전의 가치를 오름차순으로 정렬하여 작은 값부터 dp를 구할 수 있게 해주었다.

for(int i = 0; i < N; i++) {
    cin >> coin[i];
}
sort(coin.begin(), coin.end());

위에 나온 식인 dp[i] = dp[i] + dp[i-n]를 코드로 옮기면 아래와 같다.

for(int i = 0; i < N; i++) {
    for(int j = 0; j < K+1; j++) {
        if(j-coin[i] >= 0) {
            dp[j] += dp[j-coin[i]];
        }
    }
}

여기에서 1, 2, 5같은 앞에 나온 값과 상관없이 추가되는 단일 동전으로 구성된 경우의 수를 추가해주기 위해서, 현재 추가하고있는 동전의 가치와 dp의 index가 같다면 (5원을 추가하는데 5원을 구성하는 경우의 수 계산 중일 때) 동전 하나로 구성이 가능하므로 1을 추가해주는 코드를 삽입해야 한다.


for(int i = 0; i < N; i++) {
    for(int j = 0; j < K+1; j++) {
        if(j-coin[i] >= 0) {
            dp[j] += dp[j-coin[i]];
        }
        
        if(coin[i] == j) {
            dp[j] += 1;
        }
    }
}

위 반복문을 수행하고 나면 원하는 결과인 K원은 dp[K]에 저장되어있다. 이를 출력해주면 된다.

cout << dp[K];

코멘트

열심히 고민을 했지만, 풀이를 보고 말았다. 내가 생각했던 방식에서 한 발 만 더 나갔으면 된거였는데, 그 한 발을 움직이는게 쉽지 않았던 것 같다. 이거 쉬운 문제같은데... 벌써부터 이러면 어떻게 하냐 ㅠㅠ

고민

분명 쉬운 문제인데, 중복을 제거해야한다는 조건이 생각을 복잡하게 만들었던 것 같다. 또, top-down으로 풀어야 한다는 생각에 사로잡혀서 8 = 1+7 / 2+ 6 / 5+3 으로 계속 생각하다보니 자꾸 반복을 제거하지 못해서 꼬였던 것 같다. bottom-up으로 푸는 방법에 대해서도 계속 고려를 해보자.


코드

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    int N, K;
    cin >> N >> K;
    
    vector<int> coin(N);
    vector<int> dp(K+1, 0);
    
    for(int i = 0; i < N; i++) {
        cin >> coin[i];
    }
    sort(coin.begin(), coin.end());
       
    for(int i = 0; i < N; i++) {
        for(int j = 0; j < K+1; j++) {
            if(j-coin[i] >= 0) {
                dp[j] += dp[j-coin[i]];
            }
            
            if(coin[i] == j) {
                dp[j] += 1;
            }
        }
    }
    
    cout << dp[K];
    return 0;
}
320x100