티스토리 뷰

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

 

 

🤔 해결방법

1. dp 배열을 생성하고 0으로 초기화
2. 각 물건에 대해 가능한 모든 무게(j)에 대해 dp[j] 값을 갱신 (새로운 dp[j]는 기존의 dp[j]와 dp[j - 물건 무게] + 물건 가치 중 큰 값으로 갱신)
3. dp 배열의 k번째 요소 출력

 

 

🔑 풀이

처음에는 물건의 무게가 최대 무게와 같은 경우와 작은 경우로 나눠 풀었다.

 

1. 물건의 무게가 최대 무게와 같은 경우
현재 최대 가치보다 크면, 최대가치 = 그 물건의 가치

 

2. 물건의 무게가 최대 무게보다 작은 경우
최대 무게와 같거나 작아질 때까지 다음 물건들의 가치를 합침
그 값이 현재 최대 가치보다 크면, 최대 가치 = 그 값

 

이를 바탕으로 다음과 같은 코드를 짰는데 예제의 정답은 출력했으나 백준에 제출하니 런타임 에러가 떴다.😥

const input = require("fs")
    // .readFileSync("/dev/stdin")
    .readFileSync(__dirname + "/input.txt")
    .toString()
    .trim()
    .split("\n");

  const [n, stuffsMax] = input[0].split(" ").map(Number);

  // 물건들 이중 배열로 담기
  let stuffs = [];
  for (let i = 1; i <= n; i++) {
    let [stuff, value] = input[i].split(" ").map(Number);
    stuffs.push([stuff, value]);
  }

  let currentMax = 0;
  for (let i = 0; i < stuffs.length; i++) {
    // 물건의 무게가 최대 무게와 같은 경우
    if (stuffs[i][0] === stuffsMax) {
      currentMax = Math.max(currentMax, value);
    }

    // 물건의 무게가 최대 무게보다 작은 경우
    else if (stuffs[i][0] < stuffsMax) {
      let stuffSum = stuffs[i][0];
      let valueSum = stuffs[i][1];

      for (let j = i + 1; j < stuffs.length; j++) {
        if (stuffSum <= stuffsMax) {
          stuffSum += stuffs[j][0];
          valueSum += stuffs[j][1];
          if (stuffSum > stuffsMax) continue;
          currentMax = Math.max(currentMax, valueSum);
        }
      }
    }
  }
  console.log(currentMax);

문제에서 주어진 무게의 최대값이 10만이기에 주어진 물건 수가 많아지고 각 무게 값도 더 커진다면 시간복잡도가 너무 높아지기 때문인 것 같았다.

따라서 다음과 같이 DP로 풀어야 하는 문제였다.

const input = require("fs")
    // .readFileSync("/dev/stdin")
    .readFileSync(__dirname + "/input.txt")
    .toString()
    .trim()
    .split("\n");

  const [n, k] = input[0].split(" ").map(Number);
  let dp = Array(k + 1).fill(0);

  for (let i = 1; i <= n; i++) {
    const [w, v] = input[i].split(" ").map(Number);

    for (let j = k; j >= w; j--) {
      dp[j] = Math.max(dp[j], dp[j - w] + v);
    }
  }

  console.log(dp[k]);

dp 배열을 모두 0으로 초기화하고, 배열의 크기를 k + 1로 설정한다.

각 물건에 대한 정보를 순회하는데 물건을 배낭에 넣을 수 있는지 확인하고, 넣을 수 있다면 dp 테이블을 업데이트한다.

이 부분에서 물건을 넣을 수 있는 모든 가능성을 고려하기 위해 역순으로 순회하게 된다. 

dp[j]는 현재 최대 가치고 dp[j - w] + v는 새 물건을 추가했을 때의 가치이다.
둘 중 더 큰 값을 dp[j]에 저장하는 것이다.

마지막으로 dp[k]는 배낭의 무게가 k일 때의 최대 가치이므로 이 값을 출력하면 된다.

 

문제의 예제를 대입해보면 다음과 같은 과정으로 돌아간다.

아이템의 개수 n = 4
  가방의 최대 무게 k = 7

  물건 1: 무게 6, 가치 13
  물건 2: 무게 4, 가치 8
  물건 3: 무게 3, 가치 6
  물건 4: 무게 5, 가치 12
  초기 dp 배열: [0, 0, 0, 0, 0, 0, 0, 0] (k+1개의 0)

  물건 1 (무게 6, 가치 13)
  j = 7: dp[7] = max(dp[7], dp[7-6] + 13) = max(0, 0 + 13) = 13
  j = 6: dp[6] = max(dp[6], dp[6-6] + 13) = max(0, 0 + 13) = 13
  dp 배열: [0, 0, 0, 0, 0, 0, 13, 13]

  물건 2 (무게 4, 가치 8)
  j = 7: dp[7] = max(dp[7], dp[7-4] + 8) = max(13, 0 + 8) = 13
  j = 6: dp[6] = max(dp[6], dp[6-4] + 8) = max(13, 0 + 8) = 13
  j = 5: dp[5] = max(dp[5], dp[5-4] + 8) = max(0, 0 + 8) = 8
  j = 4: dp[4] = max(dp[4], dp[4-4] + 8) = max(0, 0 + 8) = 8
  dp 배열: [0, 0, 0, 0, 8, 8, 13, 13]

  물건 3 (무게 3, 가치 6)
  j = 7: dp[7] = max(dp[7], dp[7-3] + 6) = max(13, 8 + 6) = 14
  j = 6: dp[6] = max(dp[6], dp[6-3] + 6) = max(13, 0 + 6) = 13
  j = 5: dp[5] = max(dp[5], dp[5-3] + 6) = max(8, 0 + 6) = 8
  j = 4: dp[4] = max(dp[4], dp[4-3] + 6) = max(8, 0 + 6) = 8
  j = 3: dp[3] = max(dp[3], dp[3-3] + 6) = max(0, 0 + 6) = 6
  dp 배열: [0, 0, 0, 6, 8, 8, 13, 14]

  물건 4 (무게 5, 가치 12)
  j = 7: dp[7] = max(dp[7], dp[7-5] + 12) = max(14, 8 + 12) = 14
  j = 6: dp[6] = max(dp[6], dp[6-5] + 12) = max(13, 0 + 12) = 13
  j = 5: dp[5] = max(dp[5], dp[5-5] + 12) = max(8, 0 + 12) = 12
  dp 배열: [0, 0, 0, 6, 8, 12, 13, 14]

  결과: dp[7] = 14

 

'JS-algorithm > BOJ' 카테고리의 다른 글

[백준] 전화 요금 (javascript)  (0) 2023.10.19
[백준] 합 구하기 (javascript)  (0) 2023.08.21
[백준] 스택 수열 (javascript)  (0) 2023.07.25
댓글