ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 12865 평범한 배낭
    백준/배낭 문제 2022. 6. 2. 23:10

     

     

    1. 배낭 문제 알고리즘

     

    배낭 문제 알고리즘을 이용해서 풀면 된다.

     

    배낭 문제 : 배낭에 담을 수 있는 무게의 최댓값이 정해져 있을 때,

    일정한 가치, 무게가 정해져 있는 짐들을 배낭에 담아야 한다. 

    이 때 가치의 합이 최대가 되도록 짐을 고르는 방법

     

    두 가지로 분류 할 수 있는데,

     

    1) 물건을 쪼갤 수 있는 배낭 문제 => 가치가 큰 물건부터 담고, 남은 무게만큼 물건을 쪼개는 방식

                                                          => 그리디 알고리즘 사용

     

    2) 물건을 쪼갤 수 없는 배낭 문제 => dp를 활용해서 해결

     

     

    2. 문제 풀이

     

     

    이 예제는 물건을 쪼갤 수 없는 배낭 문제로 dp를 활용한다.

     

    문제 예제를 가지고 설명하겠다.

     

    4 7
    6 13
    4 8
    3 6
    5 12

     

    물품의 수(N) : 4

    배낭에 넣을 수 있는 최대 무게(K) : 7

    item 배열의 0열에는 각 물건의 무게, 1열에는 물건의 가치를 입력하겠다.

    =>

     

    물건 무게             물건 가치

    item[1][0] = 6       item[1][1] = 13  

    item[2][0] = 4       item[2][1]= 8 

    item[3][0] = 3       item[3][1] = 6

    item[4][0] = 5       item[4][1] = 12

     

    그러면 dp 를 이용해서 풀이한다.

     

    dp[i][j] = 0~i번째 물건 중 1개 이상 선택해서 그 무게 합이 j가 되었을 때

    선택한 물건들로부터 얻는 최대 가치

     

    즉, 예제에서

    1번째 물건의 무게와 가치 :  6 13 

    2번째 물건의 무게와 가치 : 4 8

    3번째 물건의 무게와 가치 : 3 6 

    4번째 물건의 무게와 가치 :  5 12

     

    0번째 물건은 없기 때문에 얻을 수 있는 가치도 없다.

    => dp 배열의 0 행렬은 모두 0이다.

     

    역시, 물건들을 선택해서 그 무게의 합이 0이 되게 만들 수 없으므로 얻을 수 있는 가치도 없다.

    => dp 배열의 0열은 모두 0이다.

     

     

    1) 1행

     

    그러면 이제 1행을 생각해보자.

    dp[1][1] = 0~1번째 물건으로 1의 무게를 만들었을 때 얻을 수 있는 최대 가치

    예제에서 주어진 물건의 무게는 6,4,3,5 이므로 1의 무게를 만들 수 없다.

    따라서 그 가치도 0이다.

     

    그러다가 dp[1][6] 이 되었을 때는

    0~1번째 물건으로 6의 무게를 만들 수 있게 된다.

    첫번째 물건의 무게가 6이며 그 가치가 13이다.

    => dp[1][6] = 13

     

    dp[1][7] 은 0~1번째 물건으로 7의 무게를 만들어야 한다.

    그러면 일단 첫번째 물건으로 6의 무게를 만든다.

    그런 다음 1만큼의 무게가 남아 있는데, 위 표에서 보면 무게 1의 최대 가치는 0이다.

    따라서 무게 6의 물건 가치 13에 무게 1의 물건 가치 0을 더해서 13 이 된다.

     

    이때, dp[0][7] = 0 과 위의 13 값 중 더 큰 수, 즉 13이 dp[1][7] 이 된다.

    이에 대한 설명은 아래에서 자세히 하겠다.

     

    2) 2행

     

     

    역시 3열까지는 dp 값이 0이다가,

    4열이 되면,

    dp[2][4] = 0~2 번째 물건 중 선택해서 무게 4를 만들었을 때 최대 가치

     

    예제에서 첫번째 물건은 무게가 6이기 때문에 안되고,

    두번째 물건은 무게가 4이다.

    그리고 그 가치가 8이므로

    dp[2][4] = 8

     

     

    dp[2][5] 역시 8이다.

     

     

    dp[2][6] = 0~2번째 물건 중 선택해서 무게 6을 만들었을 때 최대 가치인데

    첫번째 물건의 무게가 6이고, 그 가치는 13이다.

    두번째 물건의 무게는 4이고, 그 가치는 8이다.

     

    그럼 이 문제에서 사용되는 dp 식을 자세히 설명하겠다.

     i = 물건, j = 무게를 의미한다.

     

    1.  j 가 item[i][0] 보다 크거나 같을 경우

     

    이 경우는 가방에 넣을 수 있는 무게가 j인데,

    i번째 물건의 무게 (item[i][0]) 가 이보다 작거나 같아서,

    j - (i번째 물건의 무게) 만큼 다른 물건으로 채워 넣는 경우이다.

     

    => dp[i][j] = Math.max(dp[i-1][j], item[i][1] + dp[i-1][j-item[i][0]])

     

     

    2.  j 가 item[i][0] 보다 작을 경우 

     

    이 경우는 가방에 넣을 수 있는 무게 (j) 보다 

    i번째 물건의 무게 (item[i][0]) 가 커서 i번째 물건을 넣지 못한다.

    그래서 i-1번째의 dp값으로 i번째 dp값을 채워넣는 경우이다.

     

    => dp[i][j] = dp[i-1][j]

     

    dp[2][6] 은 두가지 경우 중 첫번째에 해당한다.

    그래서 

    dp[2][6] = Math.max(dp[1][6], item[2][1] + dp[1][2]) = Math.max( 13, 8+0) = 13 

     

     

    3. dp[2][6] = Math.max(dp[1][6], item[2][1] + dp[1][2])  설명

     

    1) 

     

    위의 식에서 dp[1][6] 이 어떻게 나왔을지 생각해보자.

    일단 식 dp[2][6] 에서

    0~2번째 물건 중 선택해서 무게 6을 만들어야 한다.

     

    첫번째 물건의 무게와 가치 :  6 13

    두번째 물건의 무게와 가치 : 4 8

     

    이때 첫번째 물건의 무게가 6이므로 이를 사용하면 된다.

    이 가치는 13이 된다.

    그런데 이 가치는 dp[1][6]에서 구했다.

    그렇기 때문에 dp[2][6]에서 이를 사용하는 것이다.

     

    2)

     

    item[2][1] + dp[1][2] 

    이 부분이 어떻게 나왔을지 생각해보자.

     

    첫번째 물건 말고도 두번째 물건을 사용해서 무게 6을 만들 수 있다.

    즉, 두번째 물건은 무게가 4이고, 그 가치는 8이다.

    따라서 item[2][1] = 8 을 더하는 것이고

     

    6-4=2 만큼의 무게를 더 채워야 한다.

    그런데 위의 그림에서 무게 2의 가치는 0임을 알 수 있다.

    그리고 그 값을 dp[1][2] 로 나타내었다.

     

    그러면 dp[0][2], dp[2][2] 도 있는데 dp[1][2] 를 사용한 이유가 있을 것이다.

    dp는 최대 가치를 찾고 있는 것이기 때문에, 무게 2의 가치도 최대값을 찾아야 한다.

    그렇기 때문에 dp[0][2] 보다는 dp[1][2] 가 더 값이 크다.

     

    왜냐하면 dp[0][2] 는 0번째 물건만 사용해서 2의 무게를 만들었을 때 최대 가치이고,

    dp[1][2] 는 0~1번째 물건 중 선택해서 2의 무게를 만들었을 때 최대 가치이다.

    즉 선택지가 더 많다. 그래서 그 값이 더 크다.

     

    그러면 dp[2][2]가 dp[1][2]보다 더 클 가능성이 있다.

    그런데, dp[2][2]는 0~2번째 물건 중 선택해서 2의 무게를 만들었을 때 최대가치인데,

     

     item[2][1] + dp[1][2] 

    이 식에서 알 수 있듯이,

    item[2][1] 에서 이미 2번째 물건을 사용했다.

    따라서 2번째 물건 제외 0~1번째 물건 중 선택해서 2의 무게를 만들어야 하기 때문에

    dp[1][2] 가 되는 것이다.

     

     

     

     

    3) 3행, 4행

     

     

    위에서 설명한 대로 채우면 된다.

     

     

     

     

     

    4) 

     

    위에서 dp 식이 두가지 있었다.

    그 중에서 첫번째 식은 설명했지만, 두번째는 설명하지 못했다.

    그 부분을 살펴보겠다.

     

    j 가 item[i][0] 보다 작을 경우

    => dp[i][j] = dp[i-1][j]

     

    dp[4][3]을 살펴보자

     

    j는 무게로, 3이다.

    i는 물건으로 4이다.

     

    item[4][0] = 5 라서

    j < item[4][0] 이다.

     

    즉 가방에 3무게 만큼 물건을 넣을 수 있는데,

    i번째 물건의 무게는 5로 더 크다.

    그래서 i번째 물건은 넣을 수 없다. 고려할 사항이 아니게 된다.

     

    때문에 i-1번째 dp값, 즉 0~3번째 물건 중 선택해서 무게 3을 만든 경우로

    i번째 dp값을 채우면 되는 것이다.

    => dp[4][3] = dp[3][3]

     

     

     

    3. 최종 코드

     

     

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.StringTokenizer;
    
    public class Main {
    
    	public static void main(String[] args) throws IOException {
    				
    		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    		StringTokenizer st = new StringTokenizer(br.readLine(), " ");
    		int N = Integer.parseInt(st.nextToken());
    		int K = Integer.parseInt(st.nextToken());
    		
    		int item[][] = new int[N+1][2];
    		
    		for(int i=1; i<=N;i++) {
    			st = new StringTokenizer(br.readLine(), " ");
    			item[i][0] = Integer.parseInt(st.nextToken());
    			item[i][1] = Integer.parseInt(st.nextToken());
    		}
    	
    		
    		int dp[][] = new int[N+1][K+1];
    		
    		
    		for(int i = 1; i<=N; i++) {
    			for(int j=1; j<=K; j++) {
    				if(j>= item[i][0]) {
    					
    					dp[i][j] = Math.max(dp[i-1][j],  item[i][1] + dp[i-1][j-item[i][0]]);
    					
    				}else {
    				
    					dp[i][j] = dp[i-1][j];
    			
    				}
    			}
    		}
    		
    		
    		System.out.println(dp[N][K]);
    	}
    	
    }

     

    참고 블로그 : 

    https://dheldh77.tistory.com/entry/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%B0%B0%EB%82%AD-%EB%AC%B8%EC%A0%9CKnapsack-Problem

     

    [알고리즘] 배낭 문제(Knapsack Problem)

    배낭 문제(Knapsack Problem) 배낭 문제란 배낭에 담을 수 있는 무게의 최대값이 정해져 있고, 일정한 가치와 무게가 정해져있는 짐들을 배낭에 닮을 때, 가치의 합이 최대가 되도록 짐을 고르는 방법

    dheldh77.tistory.com

    https://fbtmdwhd33.tistory.com/60

     

    [백준,BOJ 12865] 평범한 배낭(JAVA 구현, 추가풀이)

    -내 생각 이 문제의 경우 혼자 풀어보려고 해서 결과는 잘 나오는 것 같은데 어떤 반례에서 걸리는지 계속 오답처리를 받아서 결국 검색을 해봤는데, 이러한 무게와 가치가 함께 주어지는 문제

    fbtmdwhd33.tistory.com

     

    '백준 > 배낭 문제' 카테고리의 다른 글

    17845 수강 과목  (0) 2022.06.05

    댓글

Designed by Tistory.