본문 바로가기
코딩 테스트/백준

2281 데스노트 (자바스크립트)

by 위그든씨 2025. 1. 7.

문제

사악한 라이토는 기발한 방법을 이용하여 L(애칭 섊)을 살해한 뒤 데스노트를 다시 손에 넣었다. 라이토는 이제 이 노트에 n명의 이름을 적어 넣으려고 한다. 이때 다음과 같은 조건을 만족시키면서 이름을 적어 넣으려 한다.

우선, 이름을 적어 넣을 때 이미 정해진 순서대로 n명의 이름을 적어 넣어야 한다. 이름을 적을 때도, 노트를 위에서 아래로, 같은 줄에서는 왼쪽 맨 끝에서 오른쪽으로 차례로 적는다고 하자. 또한 이름을 적을 때 각 사람의 이름 사이에 빈 칸을 하나씩 두려고 한다. 한 줄을 적다가 그 줄의 끝에 한 사람의 이름이 다 들어가지 않고 잘리게 되면 반드시 새로운 줄에 이름을 써야 한다. 그렇지 않으면 이름이 중간에 잘려서 자칫하면 두 명의 사람이 죽게 된다. 이때, 각 줄의 끝에 사용하지 않고 남게 되는 칸의 수의 제곱의 합이 최소가 되도록 하려 한다. 이를 계산할 때 제일 마지막 줄은 앞으로 이름을 적을 기회가 있으므로 계산하지 않는다. 예를 들어 노트의 폭(너비)이 20인 다음의 경우를 보자.

각 사람의 이름의 길이가 차례로 7, 4, 2, 3, 2, 5, 1, 12, 7, 5, 6 인 경우이다. 위와 같이 적으면 차례로 1, 10, 0칸이 남아서 제곱의 합이 101이 된다. 반면에 아래의 경우에는 5, 6, 0칸이 남아서 제곱의 합이 61이 된다.

입력

첫째 줄에 n(1 ≤ n ≤ 1,000), m(1 ≤ m ≤ 1,000)이 주어진다. m은 노트의 가로 칸의 개수(폭, 너비)이다. 다음 n개의 줄에는 각 사람의 이름의 길이가 노트에 적어야 할 순서대로 주어진다. 각 길이는 m을 넘지 않는 자연수이다.

출력

첫째 줄에 남게 되는 칸 수의 제곱의 합의 최솟값을 출력한다.

====

문제 분석

이 문제는 각 위치에서 시작하는 최적의 해를 기록하는 것이 쟁점이다.

dp[i]는 i 번째 이름부터 시작했을 때 얻을 수 있는 최소비용이고 상태 전이는 현재 줄에 몇 개의 이름을 더 쓸수 있는지에 따라 결정된다.

즉, 점화식은 dp[i] = min(남은공간^2 + dp[j] for j in range(i+1,n) 이다 ( 현재 줄에 i~j-1번째 이름을 쓸 수 있는 경우)

function solution(idx, n) {
    if (dp[idx] < Number.MAX_SAFE_INTEGER) {
        return dp[idx];
    }
    // ... 구현 내용
}

idx 위치에서의 최적해를 구하기 위해 재귀적으로 접근할 것이고 메모이제이션을 통한 중복 계산을 방지한ㄷ.

let remain = width - names[idx];
for (let i = idx + 1; i <= n && remain >= 0; i++) {
 	//...
    remain -= names[i] + 1;
}

현재 줄에 쓸 수 있는 이름의 개수를 계산 하고 이름 사이의 공백 1칸도 고려

정답 코드

const N_MAX = 1000;
let dp = new Array(N_MAX).fill(Number.MAX_SAFE_INTEGER);
let names = new Array(N_MAX).fill(0);
function solution(idx, n) {
    if (dp[idx] < Number.MAX_SAFE_INTEGER) {
        return dp[idx];
    }
    let remain = width - names[idx];
    dp[idx] = Number.MAX_SAFE_INTEGER;
    for (let i = idx + 1; i <= n && remain >= 0; i++) {
        if (i === n) {
            dp[idx] = 0;
            break;
        }
        dp[idx] = Math.min(dp[idx], remain * remain + solution(i, n));
        remain -= names[i] + 1;
    }
    return dp[idx];
}
function minBlankCost(n, width, arr) {
    names = arr;
    dp = new Array(N_MAX).fill(Number.MAX_SAFE_INTEGER);
    dp[n - 1] = 0;
    return solution(0, n);
}
// 입력 처리
const input = require('fs')
    .readFileSync(process.platform === 'linux' ? '/dev/stdin' : './input.txt')
    .toString()
    .trim()
    .split('\n');
const [n, width] = input[0].split(' ').map((v) => +v);
const arr = [];
for (let i = 1; i < n + 1; i++) {
    arr.push(+input[i]);
}
console.log(minBlankCost(n, width, arr));

'코딩 테스트 > 백준' 카테고리의 다른 글

15990 1,2,3 더하기 5  (0) 2025.01.08
11052 카드 구매하기, 16194 카드 구매하기 2  (0) 2025.01.08
2293 동전1  (0) 2025.01.06
16932 모양 만들기  (0) 2025.01.04
13398 연속합2  (0) 2025.01.04