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
코드설명
DP에서 배낭문제(KnapSack) 알고리즘을 활용하여 진행합니다.
문제에서
dp[N+1][K+1]] : N 번째 아이템에서 K 무게별로 최대의 무게를 저장합니다.
문제에서
for(int i=1;i<=N;i++) { //몇번쨰 아이템인지를 의미합니다. for(int j=1; j<=K;j++) { // 무게를 의미합니다. dp[i][j] = dp[i-1][j]; //기본적으로 이전 아이템의 가치를 저장합니다. if( j - arr[i][0] >= 0 ) { //현재 무게에서 i번쨰 아이템의 무게 > 0 ( 즉, 본인 아이템을 담을 수 잇는지 확인 ) dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - arr[i][0] ] + arr[i][1]); } } }
- 우선 기본적으로 이전 아이템에서 해당 무게의 가치를 저장합니다. dp[i][j] = dp[i-1][j]; ( i번째 아이템의 j번째 무게일떄의 최대값에 i-1번쨰 아이템의 j일때의 최대값을 저장합니다. )
- 만약 i번쨰 무게에서 현재 무게의 값을 뺸 값이 양수라면, 해당 무게는 다른 배낭의 무게와 합쳐서 계산할 수 있다는 의미입니다.
- dp[i][j] = Math.max( dp[i-1][j], dp[i-1][j - arr[i][0] ] + arr[i][1] ) 를 통하여 더했을때의 값과 그대로의 값이 더 큰지 비교하여 갱신합니다.
- 코드에서보면 j - arr[i][0] >= 0 인지 를 확인함으로써 해당 무게가 0 이상인지 확인하고 ( 즉, j번쨰 무게와 현재 무게를 뺐을때 0 이상이라는 의미는 이전에 들었었던 최대 dp[i-1][j-arr[i][0]] 에 현재 들고 있는 배낭의 무게인 arr[i][1]을 더할 수 있느냐, 2개를 들 수 있는가, 무게를 합칠 수 있는가 ), 그것의 무게를 dp[i-1][j - arr[i][0] ] 형태로 사용하는것을 볼 수 있습니다.
코드
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; import java.util.regex.Pattern; public class Main { public static int N, K; public static String str; public static int[][] arr; public static int[][] dp; //각 무게별로 최대의 가치를 구합니다. public static int answer = 0; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); arr = new int[N+1][2]; dp = new int[N+1][K + 1]; for(int i=1;i<=N;i++) { st = new StringTokenizer(br.readLine()); arr[i][0] = Integer.parseInt(st.nextToken()); //무게 arr[i][1] = Integer.parseInt(st.nextToken()); //가치 } for(int i=1;i<=N;i++) { //몇번쨰 아이템인지를 의미합니다. for(int j=1; j<=K;j++) { // 무게를 의미합니다. dp[i][j] = dp[i-1][j]; //기본적으로 이전 아이템의 가치를 저장합니다. if( j - arr[i][0] >= 0 ) { //현재 무게에서 i번쨰 아이템의 무게 > 0 ( 즉, 본인 아이템을 담을 수 잇는지 확인 ) dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j - arr[i][0] ] + arr[i][1]); } } } System.out.println(dp[N][K]); } }
다르게 풀어본 코드입니다.
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.StringTokenizer; public class Main { public static int N, M, C, K; public static Product[] product; public static int[][] dp; public static int answer = 0; public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); N = Integer.parseInt(st.nextToken()); K = Integer.parseInt(st.nextToken()); product = new Product[N]; dp = new int[N + 1][100001]; for(int i=0;i<=N;i++) { Arrays.fill(dp[i], -1); } for(int i=0;i<N;i++) { st = new StringTokenizer(br.readLine()); int inputW = Integer.parseInt(st.nextToken()); int inputV = Integer.parseInt(st.nextToken()); product[i] = new Product(inputW, inputV); } answer = simulate(0, K); System.out.println(answer); } public static int simulate(int level, int curK) { if(curK < 0) { return 0; } if(dp[level][curK] != -1) return dp[level][curK]; if(level >= N) return 0; if(curK == 0) return 0; int resultA = 0, resultB = 0; if(curK - product[level].W >= 0) { resultA = product[level].V + simulate(level + 1, curK - product[level].W); } resultB = simulate(level + 1, curK); return dp[level][curK] = Math.max(resultA, resultB); } } class Product{ int W; int V; public Product(int W, int V) { this.W = W; this.V = V; } public Product() { } }