-
[SW 정글 26일차] Knapsack Problem 이해하기기타/SW 사관학교 정글 2021. 8. 28. 02:14
오늘 푼 문제 중에서 Dynamic Programming을 사용하여 푸는 Knapsack Problem을 만났다.
오늘 공부시간을 DP문제풀이에 쏟은 만큼 Knapsack Problem의 문제 접근법에 대해 정리해보려고 한다.
https://www.acmicpc.net/problem/12865
12865번: 평범한 배낭
첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)
www.acmicpc.net
1. 문제가 뭔데?
출처: https://en.wikipedia.org/wiki/Knapsack_problem 최대 용량이 정해진 배낭에 무게와 값어치가 다른 물건들을 담으려고 하는데 담긴 물건들의 값어치의 총합이 최대인 경우를 찾는 것이다.
위의 사진을 보면 4kg(10달러), 1kg(2달러), 2kg(2달러), 1kg(1달러) 4개의 물건을 담아서 총 15달러가 최대 값어치가 된다.
문제에 주어지는 물건들의 개수가 완전탐색을 할 수 있을 만큼 적다면 좋겠지만 그리 쉽게 풀 수 있는 문제가 아니다.
2. 문제 접근 방법
위 그림에서 1kg-1달러 물건을 제외하고 4개의 물건과 15kg 한도 배낭으로 생각해보자.
지금은 4개의 물건이여서 완전탐색으로 문제 풀이가 가능하지만 물건이 무수히 많을 경우를 생각해서 지금 주어진 문제를 하나의 큰 문제라고 생각하고 부분문제(sub problem)으로 쪼개서 접근하는 것을 생각해보자.
각 물건들은 배낭에 들어가는 경우와 들어가지 않는 경우로 나뉠 수 있다.
그러면 큰 문제가 아래의 그림과 같이 작은 문제로 나뉠 수 있다.
(A: 12Kg, 4달러/ B: 2kg, 2달러/ C: 1kg, 2달러/ D: 4kg, 10달러)
두 개의 부분 문제로 나뉘어졌고 이 부분 문제도 다시 부분 문제로 나뉠 수 있다.
끝까지 나눠보면 아래의 그림과 같다.
그림이 너무 복잡해졌는데 간단하게 말하면 각 상황에서 하나의 물건을 넣을지 말지를 결정해서 또 다른 부분 문제로 나뉘는 것이다.
결과적으로 14달러가 정답이 된다.
그러면 이 문제를 점화식으로 나타내면 어떻게 될까?
점화식을 보면 현재 배낭의 무게 한도보다 들어가고자 하는 물건의 무게가 크면 이전의 값어치를 그대로 가져간다.
그렇지 않고 물건이 들어갈 수 있는 경우에는 넣은 경우와 넣지 않은 경우 중에 큰 값을 결정한다.
3. 코드로 구현해보기
해당 문제를 코드로 구현해보면 어떻게 될까?
나는 위 문제를 top-down방식이 아닌 bottom-up방식으로 dp table을 채워가면서 최종 정답을 얻는 식으로 코드를 구현했다.
# dp 테이블 생성 # 열은 배낭의 남아있는 무게, n은 물건의 갯수 matrix = [[0 for _ in range(k + 1)] for _ in range(n + 1)] # things는 물건의 무게와 값어치를 담고 있는 배열 for i in range(1, n + 1): for j in range(1, k + 1): weight = things[i][0] value = things[i][1] #점화식 구현 if things[i][0] <= j: matrix[i][j] = max(matrix[i - 1][j], matrix[i - 1][j - weight] + value) else: matrix[i][j] = matrix[i - 1][j] print(matrix[n][k])
어제의 LCS에 이어 재밌는 문제를 마주한 것같다.
풀고 나면 재미있는데 다음 문제를 보면 또 좌절의 연속이 이어진다...
내 힘으로 풀 수 있는 DP문제가 나올 때까지 노력해보자.
[오늘의 나는 어땠을까?]
어제부터 다이나믹 프로그래밍(dp)문제를 풀고 있다.
정말 어렵다.... 답을 보지 않는 이상 어떻게 문제에 접근해야할지 감이 안온다.
분할 정복과 비슷한듯 더 어렵고 테이블을 쓴다는 것이 너무 어렵다.
그래도 계속해서 많은 문제들을 풀다보면 문제 접근법에 익숙해질 수 있다고 생각하며 계속 해보려고 한다.
내일부터는 아침에 일어나서 짧은 시간이라도 운동을 하려고 한다.
근력운동은 에너지 소비가 심할 것 같으니 런닝이라도 뛰면서 건강관리를 하려고 한다.
사실 들어오기 전부터 운동을 하려고 계획했는데 잠을 5시간정도 잘만큼 하루에 성장을 위한 주어지는 시간이 짧다고 느꼈고 어쩔 수 없이 운동을 포기했다.
운동없이도 건강히 잘 버텨왔는데 목요일부터 몸이 무기력하고 뻐근하고 더 이상 이러한 패턴으로 움직였다가는 건강때문에 공부를 못하겠다는 생각이 들었다.
그래서 주말 2틀만 아침 공부 1시간을 운동에 투자하려고 한다.
뭐든지 꾸준히 하고 싶다. 지금 계획한 운동이라는 것도.
<참고 자료>
'기타 > SW 사관학교 정글' 카테고리의 다른 글
[SW 정글 28일차] 연결리스트 만들어보기 (0) 2021.08.30 [SW 정글 27일차] 분할정복과 DP는 다른가? (0) 2021.08.29 [SW 정글 25일차] LCS는 왜 그렇게 될까? (0) 2021.08.27 SW 정글 3주차 회고 (0) 2021.08.26 [SW 정글 24일차] 플로이드 와샬 알고리즘이 궁금하다 (0) 2021.08.26