Algorithm/백준

백준12865번 : 평범한 배낭

dev-sh 2020. 11. 26. 20:35

백준12865번 : 평범한 배낭

import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class 백준12865_평범한배낭 {
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
    static StringTokenizer st = null;

    public static void main(String[] args) throws IOException{
        int n, k;
        int weight, value;
        int[][] backpack;
        ArrayList<Stuff> stuffs = new ArrayList<>();

        st = new StringTokenizer(br.readLine());
        n = Integer.parseInt(st.nextToken());
        k = Integer.parseInt(st.nextToken());
        backpack = new int[n + 1][k + 1];

        for (int i = 0; i < n; i++)
        {
            st = new StringTokenizer(br.readLine());
            weight = Integer.parseInt(st.nextToken());
            value = Integer.parseInt(st.nextToken());
            stuffs.add(new Stuff(weight, value));
        }
        Collections.sort(stuffs);

        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= k; j++)
            {
                Stuff t = stuffs.get(i - 1);
                if (t.weight > j)
                {
                    backpack[i][j] = backpack[i - 1][j];
                }
                else
                {
                    backpack[i][j] = Math.max(backpack[i - 1][j], t.value + backpack[i - 1][j - t.weight]);
                }
            }
        }
        System.out.println(backpack[n][k]);
    }

    public static class Stuff implements Comparable<Stuff>{
        int weight;
        int value;

        public Stuff(int weight, int value) {
            this.weight = weight;
            this.value = value;
        }

        @Override
        public int compareTo(Stuff o) {
            int cmp = Integer.compare(weight, o.weight);
            if (cmp == 0)
                return (Integer.compare(value, o.value));
            else
                return (cmp);
        }
    }
}

가장 기초 dp 문제며, knapsack problem 이라고 치면 많은 해설을 찾을 수 있다.

포인트는 다음과 같다.

물건 번호 물건 무게 물건 가치
1 3 6
2 4 8
3 5 12
4 6 13

주어진 물건들의 무게와 가치는 다음과 같다고 가정한다.

만약 가방의 무게가 7이라는 값으로 주어지게 되면 초기 값은 다음과 같다.

물건 번호 \ 가방 무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1 0
2 0
3 0
4 0

반복문으로 보게 된다면 1번행, 1번열부터 채우기 시작한다고 보면 된다.

점화식은 다음과 같다.

아래 점화식에서 사용할 stuff는 물건을 지칭하고, dp는 위 표를 가르킨다.

여기서 i는 행 ( 물건의 인덱스이자 표에서 행 )

여기서 j는 열 ( 가방의 무게이자, 표에서 열 )

i = 1, j = 1 부터 시작한다.

if stuff[i].무게 > j
  dp[i][j] = dp[i - 1][j];
else
  dp[i][j] = Math.max (dp[i - 1][j], stuff[i].가치 + dp[i - 1][j - stuff[i].무게])

설명보다 표의 값의 변화를 보는것이 이해가 빠를듯하다.

물건 번호 \ 가방 무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1 ( 무게 3 , 가치 6 ) 0 0 0 6 6 6 6 6
2 ( 무게 4, 가치 8 ) 0
3 ( 무게 5, 가치 12 ) 0
4 ( 무게 6, 가치 13 ) 0

i = 1, j = 1 일때, 위의 조건으로 비교해보면 1번 물건의 무게는 3 , 현재 가방 허용 무게는 j 즉 1이기때문에 if 문에서 true , 표의 (1, 1)은 (0, 1)의 값인 0

..... 쭉 가다가 j 가 3인 시점에 되면?

i = 1, j = 3 일때, 조건식 3 > 3 이 false가 되고 Math.max ( 0, 6 + 0 ) 이므로 6이 된다.

i = 2일때 보자.

물건 번호 \ 가방 무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
1 ( 무게 3 , 가치 6 ) 0 0 0 6 6 6 6 6
2 ( 무게 4, 가치 8 ) 0 0 0 6 8 8 8 14
3 ( 무게 5, 가치 12 ) 0
4 ( 무게 6, 가치 13 ) 0

표에서 ( 2, 4 )를 봤을때 값이 8로 변한다. 단순하게 생각해서 가방 무게 제한까지 하나씩 증가시키면서 가장 높은 가치가 있는 물건을 넣는 행위를 끝까지 반복하는 것임.