알고리즘 유형 : DP(Dynamic Programming), 냅색
풀이 참고 없이 스스로 풀었나요? : △
https://www.acmicpc.net/problem/2629
"use strict";
const fs = require("fs");
const input = fs.readFileSync(0).toString().trim().split("\n")
const N_w = Number(input[0])
const weight = [0].concat(input[1].split(" ").map(Number))
const N_b = Number(input[2])
const ball = input[3].split(" ").map(Number)
const knapsack = []
let max_ball = 0;
for (let i=1; i<N_w+1; i++){
max_ball += weight[i];
}
for (let i=0; i<N_w+1; i++){
knapsack.push(Array.from({length: max_ball+1}, (v, i) => ("N")));
}
for (let row=1; row<N_w+1; row++){
for (let col=1; col<max_ball+1; col++){
if (knapsack[row-1][col] === "Y"){
knapsack[row][col] = "Y";
}else if (weight[row] === col){
knapsack[row][col] = "Y";
}else{
const a = knapsack[row][col] = knapsack[row-1][Math.abs(weight[row]-col)];
const b = knapsack[row][col] = knapsack[row-1][weight[row]+col];
if (a === "Y" | b === "Y"){
knapsack[row][col] = "Y";
}else{
knapsack[row][col] = "N";
}
}
}
}
const result = [];
for (let i=0; i<ball.length; i++){
const request = ball[i];
if (request > max_ball){
result.push("N");
}else{
result.push(knapsack[N_w][request]);
}
}
console.log(result.join(" "));
for (let i=0; i<N_w+1; i++){
console.log(knapsack[i]);
}
풀이 요약
문제를 분석해보자. 두 번째 테스트케이스를 살펴보자.
4
2 3 3 3
3
1 4 10
추가 4개이고, 공의 무게가 1, 4, 10이다.
우리가 구하고자 하는 것은 추를 0~4개까지 사용해서 해당 공의 무게를 나타낼 수 있느냐는 것이다.
만약 추 4개를 활용해서(0~4개를 쓴다는 의미) 무게 8을 나타내는지 알아보고싶다면,
1) 추를 3개(2, 3, 3)를 활용해서 무게 5를 나타낼 수 있다면
, 거기에다가 더 무거운 저울쪽에 4번째 추 3을 올려버리면 무게 8을 나타내는 것이므로 오케이.
2) 추 3개를 활용해서 만약 무게 11을 나타낼 수 있다면
, 이번에는 가벼운 저울쪽에 4번째 추 3을 올려버리면 차이가 줄어드므로 무게 8을 나타내게 된다.
3) 또 다른 경우도 있는데, 추 3개를 활용해서 무게 8을 나타낼 수 있다면
, 굳이 4번째 추를 이용하지 않고 추 3개만 활용하면 된다.
4) 예외적인 경우로, 만약에 추가 4개(마지막 추는 무게가 4), 공 무게 3을 나타내고싶다고 해보자. 이 때는 추 3개로 공 무게 1을 나타낼 수 있는지 알아보면 된다. 더 가벼운 저울에 무게 4짜리 마지막 추를 올리면 그 차가 3이 된다. 그 외에는 뭐 위에서 설명한대로 추 3개로 무게 7을 나타낼 수 있는지, 3을 나타낼 수 있는지 등등을 알아보면 알아낼 수 있다. 더 가벼운 쪽에 추를 올리는 경우에 대해 이렇게 두 가지로 경우의 수가 갈리는 건데, 두 경우를 모두 포함하기 위해 abs(추무게 - 표현할 공무게)를 사용하면 된다.
이 3가지 경우중에 하나라도 만족하는 경우가 있다면, 추 4개를 활용해서 무게 8을 만들 수 있는 것이다. (Y)
추 4개에 대한 무게 확인 여부를, 추 3개 여부를 활용해서 구하므로 재귀 구조가 보인다. 점화식으로 나타내보자.
knapsack[row][col] = knapsack[row-1][col] or knapsack[row-1][abs(col-추무게)] or knapsack[row-1][col+추무게]
이 때, 리스트는 2차원인데, 행은 row이고 이는 0~row번째 추들을 활용하겠다는 것을 의미한다. 열은 col이고, 이는 추를 활용해서 나타내고자하는 임의의 공의 무게이다. knapsack[row][col]은 0번째~row번째 추들을 활용해서 무게 col을 나타낼 수 있는지 여부가 Y, N으로 담겨 있는 값이다. 점화식에서, 세 경우 중 하나라도 Y이면 knapsack[row][col] = Y인 것이다.
배운 점, 어려웠던 점