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
Knapsack 알고리즘을 사용하면 풀리는 문제이다.
최근에 알고리즘 문제 풀이에 취미가 생겨 문제를 열심히 푸는 중인데, Knapsack 알고리즘을 봐도 이해가 안되어
나름 내 방식대로 이해하고 풀다 보니 Knapsack알고리즘과 똑같이 나왔다 ㄷㄷ.
주어진 개수 = n
주어진 무게 = weight
주어진 무게의 가치 = value
Knapsack 알고리즘은 전체 무게가 weight(주어진 무게)를 넘지 않도록 n 번째까지 항목 중에서 얻어진 최고 이익을
저장하여 Dynamic Programing으로 푸는 방식이다.
처음에 이게 무슨말인지 이해가 안되어 이해를 포기하고 그냥 내 생각대로 해보니 Knapsack알고리즘이 나왔는 데,
이 글 또한 그러한 사람들을 위해 작성 또는 내가 다시보려고 작성한 글이다.
문제에 나와있는 무게와 가치
6 13
4 8
3 6
5 12
말고
아래의 가치로 계산해보고 n(주어진 개수)=5 , WEIGHT(주어진 무게의 값)=15 라고 하자
weight | value |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
5*15 로 2차원 배열을 잡는다. 실제로 생성할 땐 dp[n+1][value+1] 로 생성하자 dp[0][0~15]을 써야하기 때문.
또한. 값을 [1][1] 부터 집어넣자.
첫 번째 항목인
weight=1과 value=1을 먼저 계산한다.
weight=1과 주어진 무게의값 WEIGHT(15)까지 계산을 한다
계산은 각 열의 무게와 weight=1을 빼서 0보다 클경우 value를 집어 넣고
만약 작을 경우 그 이전의 값(바로 위의 행)을 집어넣는다. 그 이전의 값이란 것은 두번째 또는 세번째 항목부터 이해가 될 것이다.
현재 weight=1이고 첫 번째 열의 무게는 1이다 1-1>=0 이므로 1을 넣는다.
현재 weight=1이고 두 번째 열의 무게는 2이다 2-1>=0 이므로 1을 넣는다.
이런식으로 첫 번째 항목을 채운다
만약 첫 번째 항목의 weight=5라면 첫 번째 열에서는 1-5 >=0 이 아니므로 0을 넣는 데, 사실 0이라기보단 그 이전의 값(바로 위의 행)인 dp[0][1]값을 넣는 것이다. 두번째 또는 세번째 항목부터 이해가 될 것이다.
(행은 아이템 순번, 열은 무게)

두 번째 항목은 weight=2이고 value=2이다
현재 weight=2이고 첫 번째 열의 무게는 1이다 1-2>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.
이전의 값(바로 위의 행)은 dp[1][1]값인데 이는 즉, dp[n-1][j] 정도로 볼 수 있다.
현재 weight=2이고 두 번째 열의 무게는 2이다 2-2=>0 이므로 첫 번째 항목처럼 2를 넣는다라고 생각할 수 있지만
아니라 이전(바로 위의 행)의 값과 값을 비교해야한다.
이전(바로 위의 행)의 값은 1이고 현재 나의 값은 2이다 2가 더 크므로 2를넣는다.
현재 weight=2이고 세 번째 열의 무게는 3이다 3-2>=0 이고 3-2=1이다. 이제 남은 1의 값을 활용해야 한다.
이전(바로 위의 행)의 열을 사용한다.
3-2=1 이기 때문에 이전 값(바로 위의 행)의 열은 1,1 -> dp[1][1]이다
현재 weight는 2이며, value=2이고 현재 열은 세 번째 열이다. 2, 3 -> dp[2][3]
현재 열은 이전 값(바로 위의 행)의 1번열의 값 + 현재 값 -> dp[2][3] = dp[1][1]+value -> dp[2][3]=1+2=3
dp[2][3]=3이란 것을 알 수 있다.
현재 weight=2일 때 세 번째 열의 무게가 3이라는 것을 알게 되었으면 이제 또 이전의 값(바로 위의 행)과 비교를 해야한다. 바로 위의행 dp[1][3]=1 이고 현재 dp[2][3]은 3이라는 것을 알고 3이 더크므로 3을 완전히 집어 넣는다.
이로써 규칙은
1. 먼저 그 열의 무게와 현재 항목의 무게를 빼고 무게가 0보다 큰지 확인한다.
2. 0보다 작다면 이전의 값(바로 위의 행)값을 넣는다.
3. 0보다 크다면 현재 열의 무게와 현재 항목의 무게를 뺀 값을 더 한다.
더하는데 빼서 나온 값은 그 이전의 값(바로 위의 행에서 뺀 값의 열번째) 와 현재 항목의 가치를 더한다.
4. 값을 더했다면 이전의 값(바로 위의 행)의 값과 비교를 해 더 큰 값을 완전히 집어 넣는다.
이렇게 해서 두번째도 쭉 집어넣는다.

세 번째 항목은 weight=3이고 value=3이다
현재 weight=3이고 첫 번째 열의 무게는 1이다 1-3>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.
이전의 값(바로 위의 행)은 dp[2][1]값이다. 따라서 1을 넣는다
현재 weight=3이고 두 번째 열의 무게는 2이다 2-3>=0 이 아니다. 따라서 이 전의 값(바로 위의 행)을 넣어야한다.
이전의 값(바로 위의 행)은 dp[2][2]값이다. 따라서 2을 넣는다
현재 weight=3이고 세 번째 열의 무게는 3이다 3-3>=0 이다. 따라서 규칙대로 행동한다.
규칙의 3번인 0보다 크기 때문에 현재 열의 무게와 현재 항목의 무게를 뺀값을 더하는 데 3-3=0이다.
그리고 이 0의 값은 그 이전의 값( 바로 위의 행에서 뺀값의 열번째)는 dp[2][0]인데 이는 0이다.(dp[n+1][value+1] 로 잡았고 값을 [1][1]부터 잡았기때문에 0번쨰는 0으로 초기화 해준다.)
어쨋든 dp[2][0] + 현재 항목의 가치 -> dp[2][0]+3 -> 0+3 =3
그리고 규칙의 4번으로 와서 3과 그 이전의 값(바로 위의 행) dp[2][3]의 값과 비교해서 큰 값을 넣어준다. 둘이 값이 3으로 같기 때문에 3을 넣어준다.
현재 weight=4이고 네 번째 열의 무게는 4이다 4-3>=0 이다. 따라서 규칙대로 행동한다
규칙의 3번인 0보다 크기 때문에 현재 열의 무게와 현재 항목의 무게를 뺀값을 더하는 데 4-3=1이다.
그리고 이 1의 값은 그 이전의 값( 바로 위의 행에서 뺀값의 열번째)는 dp[2][1]인데 이는 1이다.
어쨋든 dp[2][1] + 현재 항목의 가치 -> dp[2][1]+3 -> 1+3 =4
그리고 규칙의 4번으로 와서 4와 그 이전의 값(바로 위의 행) dp[2][4]의 값과 비교해서 큰 값을 넣어준다.
dp[2][4]=3이고 현재는 4이기 때문에 4가 더크므로 4를 넣어준다.

이런식으로 하다보면 아래의 값들이 나온다

이게 Knapsack알고리즘인데 이를 알고리즘적으로 풀이하면
dp[i][j] = Math.max(dp[i - 1][j], value[i] + dp[i - 1][j - weight[i]]);
이렇게 된다.
BOJ 12865 평범한 배낭 문제의 최종 소스는 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <code class = "hljs java" ><span class = "hljs-keyword" > import </span> java.io.BufferedReader; <span class = "hljs-keyword" > import </span> java.io.IOException; <span class = "hljs-keyword" > import </span> java.io.InputStreamReader; <span class = "hljs-keyword" > import </span> java.util.StringTokenizer; <span class = "hljs-keyword" > public </span> <span class = "hljs-class" ><span class = "hljs-keyword" > class </span> <span class = "hljs-title" >Main</span> </span>{ <span class = "hljs-function" ><span class = "hljs-keyword" > public </span> <span class = "hljs-keyword" > static </span> <span class = "hljs-keyword" > void </span> <span class = "hljs-title" >main</span><span class = "hljs-params" >(String[] args)</span> <span class = "hljs-keyword" > throws </span> IOException </span>{ BufferedReader br = <span class = "hljs-keyword" > new </span> BufferedReader(<span class = "hljs-keyword" > new </span> InputStreamReader(System.in)); StringTokenizer st; String T; T = br.readLine(); st = <span class = "hljs-keyword" > new </span> StringTokenizer(T); <span class = "hljs-keyword" > int </span> N = Integer.parseInt(st.nextToken()); <span class = "hljs-keyword" > int </span> K = Integer.parseInt(st.nextToken()); <span class = "hljs-keyword" > int </span> weight[] = <span class = "hljs-keyword" > new </span> <span class = "hljs-keyword" > int </span>[N + <span class = "hljs-number" > 1 </span>]; <span class = "hljs-keyword" > int </span> value[] = <span class = "hljs-keyword" > new </span> <span class = "hljs-keyword" > int </span>[K + <span class = "hljs-number" > 1 </span>]; <span class = "hljs-keyword" > int </span> dp[][] = <span class = "hljs-keyword" > new </span> <span class = "hljs-keyword" > int </span>[N + <span class = "hljs-number" > 1 </span>][K + <span class = "hljs-number" > 1 </span>]; <span class = "hljs-keyword" > for </span> (<span class = "hljs-keyword" > int </span> i = <span class = "hljs-number" > 1 </span>; i < N + <span class = "hljs-number" > 1 </span>; i++) { T = br.readLine(); st = <span class = "hljs-keyword" > new </span> StringTokenizer(T); weight[i] = Integer.parseInt(st.nextToken()); value[i] = Integer.parseInt(st.nextToken()); } <span class = "hljs-keyword" > for </span> (<span class = "hljs-keyword" > int </span> i = <span class = "hljs-number" > 1 </span>; i <= N; i++) { <span class = "hljs-keyword" > for </span> (<span class = "hljs-keyword" > int </span> j = <span class = "hljs-number" > 1 </span>; j <= K; j++) { <span class = "hljs-keyword" > if </span> (j - weight[i] >= <span class = "hljs-number" > 0 </span>) { dp[i][j] = Math.max(dp[i - <span class = "hljs-number" > 1 </span>][j], value[i] + dp[i - <span class = "hljs-number" > 1 </span>][j - weight[i]]); } <span class = "hljs-keyword" > else </span> { dp[i][j] = dp[i - <span class = "hljs-number" > 1 </span>][j]; } } } System.out.println(dp[N][K]); } } </code> |