• TOC {:toc}

이 글은 백준 온라인 저지의 12865번 문제를 풀이한 것을 모아놓은 글입니다.

일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.

2021.05.06 (Python)

메모리 시간 코드 길이
225444 KB 5620 ms 371 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 23:41:32 23:42:16
풀이 생각 1 23:43:23 23:51:50
풀이 생각 2 11:45:36 11:58:02
코딩 11:58:10 12:06:43
디버깅 12:07:54 12:42:57
import sys
input = lambda: sys.stdin.readline().rstrip()

N, K = map(int, input().split())
Vs = [[0] * (K + 1) for _ in range(N)]

for i in range(N):
    W, V = map(int, input().split())

    for j in range(1, K + 1):
        if j < W:
            Vs[i][j] =  Vs[i - 1][j]
        else:
            Vs[i][j] = max(Vs[i - 1][j], Vs[i - 1][j - W] + V)

print(Vs[N - 1][K])

아이디어 & 풀이

1부터 K까지 각 무게에서 들 수 있을는 최대 가치합을 저장한다.

W V 1 2 3 4 5 6 7
6 13 0 0 0 0 0 13 13
4 8 0 0 0 8 8 13 13
3 6 0 6 6 8 8 13 14
5 12 0 6 6 8 12 13 14
  • W를 인덱스로, 최대 가치 합 V를 값으로 갖는 리스트를 구성한다.

  • 입력받은 W보다 큰 W값에 대해서 V를 비교하면서 입력한 V가 더 크면 교체한다.

    for i > W:
        V vs F[i] vs F[i-W] + V
    

디버그

  • 각 원소가 바로바로 변하니까 이전 단계를 별도로 저장해주어야 한다.
    • Vs[3]의 값을 앞에서 변경하고 Vs[7]의 값을 정할 때 Vs[3] + Vs[4]를 비교하면 Vs[3]은 변한 상태이므로 제대로 된 최댓값을 계산할 수 없다.
    • 현재 상태에서 이전의 Vs값들을 유지하려면 이를 별도로 저장해주어야 한다.
    • Vs를 이차원 배열로 초기화하고 업데이트 때마다 다음 배열에 저장한다.

피드백

  • max를 계산할 때 Vs[i - 1]V를 더한 값이 항상 V보다 크거나 같기 때문에 V는 고려할 필요가 없다.
  • 마지막에 Vsmax를 계산하지 않고 K를 출력하는게 더 빠르다. (어차피 K의 값이 최댓값이다)

2022.04.03 (JS)

메모리 시간 코드 길이
200692 KB 1192 ms 694 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 17:47:17 17:48:18
풀이 생각 16:09:42 16:16:05
코딩 16:16:07 16:39:49
디버깅 17:26:03 17:30:13
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().trim().split("\n");

const [NK, ...items] = input;
const [N, K] = NK.split(" ").map(Number);
bag = { 0: 0 }; // weight: value

function main() {
    const new_bag = { 0: 0 };

    items.forEach((item) => {
        const [W, V] = item.split(" ").map(Number);
        for (let [w, v] of Object.entries(bag)) {
            w = parseInt(w);
            if (w + W <= K) {
                if (bag[w + W]) new_bag[w + W] = Math.max(bag[w + W], v + V);
                else new_bag[w + W] = v + V;
            }
        }
        bag = new_bag;
    });

    return Math.max(...Object.values(bag));
}

console.log(main());

디버그

  • 또 입력받을 때 trim() 안 적어서 틀렸다.

피드백

  • input 입력 받을 때 아예 map으로 전부 다 숫자로 변환하고 하는 게 깔끔했을 것 같다.

참고 답안 1

import sys
input = sys.stdin.readline

N, K = map(int, input().split())
KnapSack = {0: 0}
for _ in range(N):
    W, V = map(int, input().split())
    tmp = {}
    # KanpSack(배낭) 딕셔너리 안의 각 key(무게, w)와 value(가치, v)에 대해서
    for w, v in KnapSack.items():
        # 현재 아이템과 입력받은 무게의 합(w + W)이 K를 넘지 않고
        # 현재 아이템과 입력받은 가치의 합(v + V)이 두 무게를 합친 것의 가치 최댓값(KanpSack[w + W])보다 크면
        if w + W <= K and v + V > KnapSack.get(w + W, 0): 
            # tmp에 해당 무게의 가치 최댓값을 현재 값으로 지정한다.
            tmp[w + W] = v + V

    # KnapSack을 tmp로 업데이트한다.
    KnapSack.update(tmp)

# KnapSack의 value(가치) 중에서 최댓값을 출력한다.
print(max(KnapSack.values()))

아이디어 & 풀이

기본적인 과정은 위의 답과 비슷하다.

리스트 대신 딕셔너리를 사용해 현재 KanpSack 안에 존재하는 아이템에 대해서만 계산한다. 반복을 최소화할 수 있다.

변하는 부분만 tmp를 먼저 구성한 뒤 tmp로 기존의 KanpSack을 업데이트해서 사용하는 메모리를 최소화한다.

참고 답안 2

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

const bag = input.map((str) => str.split(" ").map(Number));
const [N, K] = bag.shift();

function solution() {
  const matrix = Array(K + 1).fill(0);

  bag.forEach(([w, v]) => {
    for (let i = K; i >= w; i -= 1) {
      matrix[i] = Math.max(matrix[i], matrix[i - w] + v);
    }
  });

  return matrix[K];
}

console.log(solution());

풀이 분석해서 적기