동전 1
0.5 초 (추가 시간 없음) | 4 MB | 53544 | 24589 | 18580 | 45.903% |
문제
n가지 종류의 동전이 있다. 각각의 동전이 나타내는 가치는 다르다. 이 동전을 적당히 사용해서, 그 가치의 합이 k원이 되도록 하고 싶다. 그 경우의 수를 구하시오. 각각의 동전은 몇 개라도 사용할 수 있다.
사용한 동전의 구성이 같은데, 순서만 다른 것은 같은 경우이다.
입력
첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.
출력
첫째 줄에 경우의 수를 출력한다. 경우의 수는 231보다 작다.
문제풀이
역시 dp문제는 악마다. 코드는 굉장히 짧은데 그 과정을 이해하는 것이 매우 매우 버겁다.
처음 풀이
[101][10001] 짜리 배열을 만들어 풀어보려고 했다.
그런데 문제의 메모리 제한이 4MB라서 저 배열을 만들면 이미 사용하는 메모리가 8*101*10001 byte 이므로 8MB가 되어 메모리 초과가 뜨게 된다.
그래도 어떤 생각을 갖고 구현했는지 적어본다.
각 배열의 값은 어떤 식이냐면, 행은 입력받은 동전의 가치를 나타내고, 10001은 현재까지 사용한 금액을 나타낸다.
이 배열 안에는 경우의 수가 들어가게 된다.
글로 설명하려니 무지무지 어렵다.
점화식은 다음과 같다.
dp[i][j] = dp[i][j] + dp[i-1][j]
만약 j 가 동전의 가치보다 크다면
dp[i][j] = dp[i][j] + dp[i-1][j] + dp[i][j-동전의가치]
자기 자신 + 자기 이전 동전의 가치 인덱스에 있는 값 + 자기 자신 - 동전의 가치 인덱스에 있는 값
그러니까 지금 현재 경우의 수를 구하려면 현재 자기자신 (자기 자신이 1인 경우가 존재하므로)에 자기자신에 자기 자신 코인 가치를 뺀 값 (ex 현재 2원 동전이고 4원일 경우, 2원 동전을 2개쓰는 경우를 가져와야 함. 즉 2원동전을 1개 쓰는 인덱스에서 가져와야 함)에다가 자기 이전 동전 가치의 값을 더하면 현재 경우의 수를 구할 수 있다 (말 그대로 자기 이전 동전 사용한거의 경우의 수임).
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
2 | 1 | 2 | 2 | 3 | 3 | 4 | 4 | 5 | 5 | 6 |
5 | 1 | 2 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 |
#include <iostream>
using namespace std;
int dp[101][10001];
int main() {
int n, k;
cin >> n >> k;
int arr[n];
for(int i=0; i<n; i++) {
cin >> arr[i];
dp[arr[i]][arr[i]] = 1;
}
for(int i=0; i<n; i++) {
for(int j=1; j<=k; j++) {
cout << dp[arr[i]][j] << " ";
}
cout << endl;
}
int before = 0;
for(int i=0; i<n; i++) {
for(int j=1; j<=k; j++) {
if(j-arr[i]>0) {
dp[arr[i]][j] = dp[arr[i]][j] + dp[arr[i]][j-arr[i]] + dp[before][j];
}
else {
dp[arr[i]][j] = dp[arr[i]][j] + dp[before][j];
}
}
before = arr[i];
}
// for(int i=0; i<n; i++) {
// for(int j=1; j<=k; j++) {
// cout << dp[arr[i]][j] << " ";
// }
// cout << endl;
// }
cout << dp[arr[n-1]][k];
return 0;
}
그런데 메모리 초과이기 떄문에 이걸 어떻게 해결할지 고민해봤다.
생각해보니 이런 방식이면 하나의 배열로 모두 처리가 가능할거라고 생각하여 101 부분을 빼주고 코드를 조금 수정했더니 성공이 나왔다.
여담이지만 내 기억으론 다익스트라나 플로이드 알고리즘도 이와같이 덮어쓰기 방식으로 구현하면 메모리를 상당히 아낄수 있다고 들었다.
#include <iostream>
using namespace std;
int dp[10001];
int main() {
int n, k;
cin >> n >> k;
int arr[n];
for(int i=0; i<n; i++) {
cin >> arr[i];
}
for(int i=0; i<n; i++) {
for(int j=1; j<=k; j++) {
if(j == arr[i]) dp[j] += 1;
if(j-arr[i]>0) {
dp[j] = dp[j] + dp[j-arr[i]];
}
}
}
cout << dp[k];
return 0;
}
'알고리즘(Algorithm)' 카테고리의 다른 글
[C++] 미세먼지 안녕! (17144번) (3) | 2023.06.02 |
---|---|
[C++] 숫자 야구 (2503번) (0) | 2023.05.15 |
[C++] 스도쿠 (2580번) (0) | 2023.05.02 |
[C++] 후보 추천하기 (1713번) (0) | 2023.04.28 |
[C++] 나이트 투어 (1331번) (0) | 2023.04.16 |