[알고리즘공부] [자료구조공부] 냅색 알고리즘
Knapsack
🎀 본 게시물은 코딩테스트를 위한 자료구조, 알고리즘 공부 게시물입니다. 또한 다음 강의자료를 사용했음을 알려드립니다.
인프런강의 🎀
냅색 알고리즘에 대해 알아보고 예시 문제를 통해 익히자.
냅색 알고리즘이 뭐지?
냅색 알고리즘은 한정된 가방에 최대한 많은 물건을 담아야 할 때, 각 물건의 가치와 부피를 고려하여 어떤 물건들을 선택해야 최대 가치를 얻을 수 있는지를 구하는 알고리즘입니다.
냅색 알고리즘은 0/1 냅색문제와 Fractional 냅색문제로 나뉩니다. 0/1 냅색문제는 물건을 담거나 안담거나 둘 중 하나만 선택할 수 있는 문제이고, Fractional 냅색문제는 물건을 일부분만 쪼개서 담을 수 있는 문제입니다.
이 알고리즘은 동적 계획법을 사용하여 구현할 수 있으며, 시간 복잡도는 O(NW)입니다.여기서 N은 물건의 개수이고, W는 가방의 최대 부피입니다.
이 알고리즘은 대표적으로 백준 온라인 저지의 “짐 싸기 문제(Knapsack)”와 같은 다양한 문제에서 적용됩니다.
1. 가방문제 (냅색 알고리즘)
최고 17kg의 무게를 저장할 수 있는 가방이 있다. 그리고 각각 3kg, 4kg, 7kg, 8kg, 9kg의 무게를 가진 5종류의 보석이 있다. 이 보석들의 가치는 각각 4, 5, 10, 11, 13이다.
이 보석을 가방에 담는데 17kg를 넘지 않으면서 최대의 가치가 되도록 하려면 어떻게 담아야 할까요? 각 종류별 보석의 개수는 무한이 많다. 한 종류의 보석을 여러 번 가방에 담을 수 있다는 뜻입니다.
▣ 입력설명
첫 번째 줄은 보석 종류의 개수와 가방에 담을 수 있는 무게의 한계값이 주어진다. 두 번째 줄부터 각 보석의 무게와 가치가 주어진다. 가방의 저장무게는 1000kg을 넘지 않는다. 보석의 개수는 30개 이내이다.
▣ 출력설명
첫 번째 줄에 가방에 담을 수 있는 보석의 최대가치를 출력한다.
▣ 입력예제 1
4 11
5 12
3 8
6 14
4 8
▣ 출력예제 1
28
설명
- 다이나믹 테이블 dy를 가방에 담을 수 있는 무게의 한계값을 길이의 리스트로 만들고 모두 0으로 초기화
- dy[j]는 j무게만큼 담을 수 있을때 총 가치를 저장해둔다.
- 5kg일때 5kg하나를 넣은 가치랑 3kg, 2kg 보석 나눠서 내는 가치를 가 이득인지 업데이트하면서 11까지 가면 된다.
🚀 나의 풀이 ⭕
import sys
sys.stdin = open('input.txt', 'r')
if __name__=="__main__":
n,m=map(int, input().split()) # 보석 종류 개수, 무게의 한계값
dy=[0]*(m+1)
for _ in range(n):
w,v = map(int, input().split()) # 보석 무게, 보석 가치
for j in range(w,m+1): # j는 가방 무게
dy[j] = max(dy[j],dy[j-w]+v) # w는 보석 무게, v는 보석 가치
# print(dy)
print(dy[m]) # 정답
2. 동전교환 (냅색 알고리즘)
다음과 같이 여러 단위의 동전들이 주어져 있을때 거스름돈을 가장 적은 수의 동전으로 교환 해주려면 어떻게 주면 되는가? 각 단위의 동전은 무한정 쓸 수 있다.
▣ 입력설명
첫 번째 줄에는 동전의 종류개수 $N(1<=N<=12)$이 주어진다. 두 번째 줄에는 N개의 동전의 종 류가 주어지고, 그 다음줄에 거슬러 줄 금액 $M(1<=M<=500)$이 주어진다. 각 동전의 종류는 100원을 넘지 않는다.
▣ 출력설명
첫 번째 줄에 거슬러 줄 동전의 최소개수를 출력한다.
▣ 입력예제 1
3
1 2 5
15
▣ 출력예제 1
3
설명
- 거슬러줄 금액 M 길이만큼의 dy 리스트에 1000으로 초기화해서 만들기
- 동전 종류별로 for문 반복
- 해당 동전값부터 거슬러줄 금액 M까지 반복하며 동전개수 dy에 넣기
- 지금 넣어져 있는거랑 업데이트할거랑 비교하면서 업데이트
🚀 나의 풀이 ⭕
import sys
sys.stdin = open('input.txt', 'r')
if __name__=="__main__":
n=int(input()) # 동전 종류의 개수
coins = list(map(int, input().split()))
m=int(input())
dy=[1000]*(m+1)
dy[0]=0
for i in range(len(coins)):
# print("coin: ",coins[i])
for j in range(coins[i],m+1): # 1~15, 2~15, 5~15
dy[j]=min(dy[j],dy[j-coins[i]]+1)
# print(dy)
print(dy[m])
3. 최대점수 구하기 (냅색 알고리즘)
이번 정보올림피아드대회에서 좋은 성적을 내기 위하여 현수는 선생님이 주신 N개의 문제를 풀려고 합니다. 각 문제는 그것을 풀었을 때 얻는 점수와 푸는데 걸리는 시간이 주어지게 됩 니다. 제한시간 M안에 N개의 문제 중 최대점수를 얻을 수 있도록 해야 합니다. (해당문제는 해당시간이 걸리면 푸는 걸로 간주한다, 한 유형당 한개만 풀 수 있습니다.)
▣ 입력설명
첫 번째 줄에 문제의 개수 $N(1<=N<=100)$과 제한 시간 $M(10<=M<=1000)$이 주어집니다. 두 번째 줄부터 N줄에 걸쳐 문제를 풀었을 때의 점수와 푸는데 걸리는 시간이 주어집니다.
▣ 출력설명
첫 번째 줄에 제한 시간안에 얻을 수 있는 최대 점수를 출력합니다.
▣ 입력예제 1
5 20
10 5
25 12
15 8
6 3
7 4
▣ 출력예제 1
41
설명
- 한 유형당 하나만 풀 수 있다. 동전문제처럼 무한히 넣을 수 없다.
- 제한시간 M 길이만큼의 dy 리스트를 만들고 뒤에서부터 진행하며
- 점수와 시간 입력값을 받으면서 채워넣자
🚀 나의 풀이 ⭕
import sys
sys.stdin = open('input.txt', 'r')
if __name__=="__main__":
n,m=map(int, input().split())
dy=[0]*(m+1)
for i in range(n):
score, time = map(int, input().split())
for j in range(m,time-1,-1):
dy[j]=max(dy[j],dy[j-time]+score)
# print(dy)
print(dy[m])
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄