문제 주소 : www.acmicpc.net/problem/2294
정답 비율 : 28.169% (2021.03.03 22:47 기준)
2294번: 동전 2
첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다. 가치가 같은 동전이 여러 번 주
www.acmicpc.net
문제 요약 :
백준 : 2293, 동전 1
문제 주소 : www.acmicpc.net/problem/2293 정답 비율 : 43.670% (2021.03.01 18:48 기준) 2293번: 동전 1 첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치..
newbie22.tistory.com
동전 1 문제와 아주 유사합니다. 다른 점은 출력하는 것은 최소로 사용하는 동전의 개수이며 이때 각 동전의 가치가 중복으로 주저질 수 있습니다.
문제 조건 :
입력 : 첫 줄에는 N(1 ~ 100) , K(1 ~ 10000) 두 개 값이 주어집니다. 그 후 N개의 1 ~ 100000 사이의 값이 주어집니다. 동전의 값어치에 대하여 중복 값이 존재할 수 있습니다.
출력 : 동전의 최소 개수를 출력합니다. 이때 불가능 경우에는 -1을 출력합니다.
시간 : 1 초
문제 풀이 :
이 또한 DP 문제입니다. 아이디어는 다음과 같습니다.
1. 우선 N개의 동전을 한 개씩에 데이터를 구축합니다.
2. DP에는 1 ~ K 만들 수 있는 경우 중 가장 적게 드는 경우를 저장합니다. 이때의 데이터는 1 ~ n 번째 동전을 사용한 경우입니다.
3. 동전으로 k 값(1 ~ K)을 만들기 위하여 해당 동전(n, 가치 : v)이 최대로 들어갈 수 있는 개수(T)를 구합니다.
4. DP[k - v * t] + t (t : 1 ~ T) 중 DP[k - v * t]가 0이 아닌 최솟값을 구합니다. (DP[k - v * t]가 0이 아닌 이유는 해당 값이 0이면 이는 1 ~ n - 1까지 동전을 사용하여 k - v * t 인 경우가 존재하지 않음을 의미하기 때문입니다.)
5. 해당 최솟값과 DP[k]를 비교하여 더 작은 값을 구합니다. (단, DP[k]가 0이 아닌 경우, DP[k]가 0이면 위에서 구한 최솟 값이 DP[k] 값이 되겠습니다.)
코드는 다음과 같습니다.
C99
#include<stdio.h>
int DP[10001] = {0, };
int coin[100];
int visit[100000] = {0, };
int min(int a, int b)
{
if(a == 0) return b;
else if(b == 0) return a;
else if(a > b) return b;
else return a;
}
int main()
{
int N, K, i = -1, j = -1;
scanf("%d %d", &N, &K);
for(i = 0; i < N; i++)
scanf("%d", &coin[i]);
for(i = 0; i < N; i++)
{
if(visit[coin[i] - 1]++ == 0)
{
for(j = 1; j <= K; j++)
{
int multi = j / coin[i], z = -1;
for(z = multi; z > 0; z--)
{
if(j - z * coin[i] == 0 || DP[j - z * coin[i]] != 0)
DP[j] = min(DP[j], DP[j - z * coin[i]] + z);
}
}
}
}
printf("%d\n", (DP[K] ? DP[K] : -1));
return 0;
}
-> 이때 visit을 사용하는 이유는 중복 값이 주어지기 때문입니다.
하지만 해당 방법을 보면 K값에 대하여 botton-up방식으로 DP를 구성하지만 실질적으로 DP값을 구축하는 데 있어서 multi 값을 사용하여 top-down 방식을 사용하는 것을 확인할 수 있습니다.
좀 더 효율적인 방법이 있습니다. 이는 DP[k]에 K + 1의 값을 저장하는 것입니다. 그리고 조사 조건을
coin의 value >= k이고 DP[k] > DP[k - coin의 value]를 만족하면 됩니다.
해당 방식을 사용하면 DP[k - coin의 value]가 1 ~ n번째 coin을 사용하여 만들 수 없는 값이면 최대 값인 K + 1 인 값이 저장되고 이는 DP[k] 값이 K + 1 보다 클 수 없기 때문입니다.
이때 해당 실행에서 DP의 최댓값이 K + 1인 이유는 coin의 최솟값이 1이고 1을 이용하여 DP[K]를 구축하면 DP[K]의 최솟값은 K이기 때문입니다.
코드는 다음과 같습니다.
C99
#include<stdio.h>
int DP[10001] = {0, };
int coin[100];
int visit[100000] = {0, };
int main()
{
int N, K, i = -1, j = -1;
scanf("%d %d", &N, &K);
for(i = 0; i < N; i++)
scanf("%d", &coin[i]);
for(i = 1; i <= K; i++)
DP[i] = K + 1;
for(i = 0; i < N; i++)
{
if(visit[coin[i] - 1]++ == 0)
{
for(j = 1; j <= K; j++)
{
if(j >= coin[i] && DP[j] > DP[j - coin[i]])
DP[j] = DP[j - coin[i]] + 1;
}
}
}
printf("%d\n", (DP[K] != K + 1 ? DP[K] : -1));
return 0;
}
***
동전 1과 동전 2 문제를 풀면서 아직 DP문제에서 뭐를 DP 구축해야 할지는 감을 잡았지만 아직 효율적인 DP구축에 대하여 부족함을 느꼈습니다.
'Algorithm Trainning > 백준 알고리즘(BEAKJOON)' 카테고리의 다른 글
| 백준 : 1912번, 연속합 (0) | 2021.03.17 |
|---|---|
| 백준 : 3062번, 수 뒤집기 (0) | 2021.03.15 |
| 백준 : 2293, 동전 1 (0) | 2021.03.01 |
| 백준 : 2606번, 바이러스 (0) | 2021.02.28 |
| 백준 : 1629번, 곱셈 (0) | 2021.02.27 |