반응형
05-15 16:06
Today
Total
«   2024/05   »
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
관리 메뉴

개발하는 고라니

[백준] 12865번 : 평범한 배낭 본문

Programming/백준

[백준] 12865번 : 평범한 배낭

조용한고라니 2021. 1. 19. 02:57
반응형
 

12865번: 평범한 배낭

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)

www.acmicpc.net

# 문제

 

이 문제는 아주 평범한 배낭에 관한 문제이다.

한 달 후면 국가의 부름을 받게 되는 준서는 여행을 가려고 한다. 세상과의 단절을 슬퍼하며 최대한 즐기기 위한 여행이기 때문에, 가지고 다닐 배낭 또한 최대한 가치 있게 싸려고 한다.

준서가 여행에 필요하다고 생각하는 N개의 물건이 있다. 각 물건은 무게 W와 가치 V를 가지는데, 해당 물건을 배낭에 넣어서 가면 준서가 V만큼 즐길 수 있다. 아직 행군을 해본 적이 없는 준서는 최대 K만큼의 무게만을 넣을 수 있는 배낭만 들고 다닐 수 있다. 준서가 최대한 즐거운 여행을 하기 위해 배낭에 넣을 수 있는 물건들의 가치의 최댓값을 알려주자.

 

# 입력

 

첫 줄에 물품의 수 N(1 ≤ N ≤ 100)과 준서가 버틸 수 있는 무게 K(1 ≤ K ≤ 100,000)가 주어진다. 두 번째 줄부터 N개의 줄에 거쳐 각 물건의 무게 W(1 ≤ W ≤ 100,000)와 해당 물건의 가치 V(0 ≤ V ≤ 1,000)가 주어진다.

입력으로 주어지는 모든 수는 정수이다.

 

# 출력

 

한 줄에 배낭에 넣을 수 있는 물건들의 가치합의 최댓값을 출력한다.

 

# 예시 입력

4 7
6 13
4 8
3 6
5 12

# 예시 출력

14

배낭 문제는 1) 그리디 방법과 2) 동적 프로그래밍 방법으로 해결할 수 있다.

하지만 그리디 방법이 항상 최적해를 찾아주는 것은 아니다.

물건을 쪼갤 수 있는 배낭문제(Fraction Knapsack Problem)는 그리디 방법으로 최적해를 구할 수 있다.

이와 다르게 물건을 쪼갤 수 없는 배낭문제(0/1 Knapsack Problem)는 동적 프로그래밍 방법으로 해결해야 한다.

 

이 문제는 0/1 배낭 문제이므로 동적 프로그래밍을 이용하여 최적해를 구해보자.

 

입력으로 다음과 같은 수가 주어졌다고 하자.

5 7       //물건의 개수 : 5, 배낭의 capacity : 7
3 5       //물건 1의 무게 : 3, 가치 : 5
2 9       //물건 2의 무게 : 2, 가치 : 9
7 10     //물건 3의 무게 : 7, 가치 : 10
5 6      //물건 4의 무게 : 5, 가치 : 6
6 8      //물건 5의 무게 : 6, 가치 : 8

동적 프로그래밍으로 해결하기 위해선 물건의 개수(n)와 배낭의 무게(k)를 이용해 2차원 배열을 준비해야 한다.

(n+1) x (k+1) 테이블을 준비하자. (자연스럽게 0행과 0열은 0으로 가득 채워주자)

    무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
물건 1 (3) 0 0 0 5 5 5 5 5
물건 2 (2) 0              
물건 3 (7) 0              
물건 4 (5) 0              
물건 5 (6) 0              

* Row = 물건 1 (무게3, 가치 5)

DP(1, 1~2) : 무게 1~2인 곳에는 무게가 3인 물건1을 넣을 수 없으니 DP(0, 1)의 값을 가져온다.

DP(1, 3) : 무게 3인 곳에 물건 1을 넣을 수 있으니 조건을 고려해야 한다.

 

(현재 값 + 한 칸위의 (현재 무게 - 물건 1의 무게)인 곳의 값) vs 한 칸 위의 값

= Math.max( value[i] + dp[i-1][j-weight[i]], dp[i-1][j] )

value[1] + dp[0][3 - weight[1]] = 5 + 0 = 5

dp[0][3] = 0

이므로 DP(1, 3) = 5가 된다.

마찬가지로 DP(1, 4~7) 은 5를 가져간다.


    무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
물건 1 (3) 0 0 0 5 5 5 5 5
물건 2 (2) 0 0 9 9 9 14 14 14
물건 3 (7) 0              
물건 4 (5) 0              
물건 5 (6) 0              

* Row = 물건 2 (무게2, 가치 9)

DP(2, 1) : 무게가 2는 넘어야 물건을 넣을 수 있으므로 위의 값을 가져온다.

DP(2, 2) : 무게가 2 이므로 물건 2를 넣을 수 있다.

Math.max(9 + DP[1][0], DP[1][2]) = 9

DP(2, 3~4) 도 마찬가지로 계산해서 값을 넣어준다.

 

DP(2, 5) : 처음 맞이하는 케이스이다. 무게 5 중에서 물건 2를 넣고 나면 3이 남으니 물건 1을 하나 더 넣을 수 있는 상황이다. 따라서 점화식에 의해 다음과 같은 식을 세울 수 있다.

Math.max(value[2] + DP[2-1][5-weight[2]], DP[2-1][5])

Math.max(9 + 5, 5) = 14

이므로 14를 가질 수 있다.


    무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
물건 1 (3) 0 0 0 5 5 5 5 5
물건 2 (2) 0 0 9 9 9 14 14 14
물건 3 (7) 0 0 9 9 9 14 14 14
물건 4 (5) 0              
물건 5 (6) 0              

* Row = 물건 3 (무게 7, 가치 10)

DP(3, 1~6) : 이 물건은 무게가 7이므로 무게가 6인 곳 까지는 위의 값을 그대로 가져와야 한다. 

DP(3, 7) : 무게가 7인 곳에 이 물건을 넣을 수 있으나, 점화식대로 계산하면 최대값은 14이므로 의미가 없다.


    무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
물건 1 (3) 0 0 0 5 5 5 5 5
물건 2 (2) 0 0 9 9 9 14 14 14
물건 3 (7) 0 0 9 9 9 14 14 14
물건 4 (5) 0 0 9 9 9 14 14 15
물건 5 (6) 0              

* Row = 물건 4 (무게 5, 가치 6)

무게 < 5인 곳 까진 한 칸위의 값을 가져온다.

 

DP(5, 5~6) : 물건 5를 넣는 것과, 물건 1 + 물건 2을 넣는 것을 비교해서 값이 들어가게 되는데 더 큰 값인 14가 들어간다.

DP(5, 7) : 물건 5를 넣고 무게 2가 남으므로 (물건 5 + 물건 2) vs (물건 2 + 물건 1)을 비교해서 더 큰 값인 15가 들어간다.


    무게 0 1 2 3 4 5 6 7
0 0 0 0 0 0 0 0 0
물건 1 (3) 0 0 0 5 5 5 5 5
물건 2 (2) 0 0 9 9 9 14 14 14
물건 3 (7) 0 0 9 9 9 14 14 14
4 (5) 0 0 9 9 9 14 14 15
물건 5 (6) 0 0 9 9 9 14 14 15

표를 완성하면 이렇게 된다. 따라서 문제에서 구하고자 하는 답은 15가 된다.

 

# 알고리즘

/*
K : 배낭의 capacity
N : 물건의 개수
w[] : 각 물건의 무게
v[] : 각 물건의 가치
*/
function knapSack(K, N, w[], v[]){

	int[][] dp = new int[N+1][K+1]

	for i=1 -> N
    	   for j=1 -> K
        	if(w[i] <= j)
            	dp[i][j] = MAX(v[i] + dp[i-1][j-w[i]], dp[i-1][j]
            else
            	dp[i][j] = dp[i-1][j]
                
    return dp[i][j] 중 최대값
}

# Whole Code </> -Java 11

public class Main {

    private static int knapSack(int[] val, int[] w, int n, int k) {

        int[][] dp = new int[n+1][k+1];
        int max = 0;

        for(int i=1; i<=n; i++)
            for(int j=1; j<=k; j++){
            
                if(w[i] <= j)
                    dp[i][j] = Math.max(val[i] + dp[i-1][j-w[i]], dp[i-1][j]);
                
                else
                    dp[i][j] = dp[i-1][j];
                
                max = Math.max(max, dp[i][j]);
            }
        
        
        return max;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int N = sc.nextInt(); //물건의 개수
        int K = sc.nextInt(); //배낭이 견디는 무게
        
        int[] val = new int[N+1];
        int[] w = new int[N+1];
        
        for(int i=1; i<=N; i++){
            w[i] = sc.nextInt(); //무게
            val[i] = sc.nextInt(); //가치
        }
        System.out.printf("%d\n", knapSack(val, w, N, K));
    }
}
반응형
Comments