• TOC {:toc}

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

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

2021.06.22 (Python)

메모리 시간 코드 길이
29200 KB 68 ms 476 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 14:13:14 14:15:16
풀이 생각 1 14:15:18 15:57:50
코딩 1 15:58:32 16:32:39
풀이 생각 2 10:39:40 10:56:26
코딩 2 11:02:27 12:27:38
디버깅 15:10:05 16:05:31
import sys
input = sys.stdin.readline

B = []
for i in range(int(input())):
    B.append((i + 1, *map(int, input().split())))
# 블록을 무게에 대해 내림차순으로 정렬한다.
B.sort(key=lambda x: x[-1], reverse=True)

T = []
for bn, ba, bh, _ in B:
    # 높이의 초깃값으로 받아온 블록의 높이를 지정한다.
    h_max = bh
    # 쌓은 블록 번호는 초깃값을 빈 리스트로 지정한다.
    # 지금까지 쌓은 블록 번호에 현재 블록의 번호를 추가할 예정으로
    # 처음에 쌓인 블록은 없기 때문에 빈 리스트다.
    n_max = []
    # 현재 만든 탑을 순회하면서 각 높이, 최소밑면, 쌓은 블록 번호에 대해
    for th, ta, tns in T:
        # 탑의 최소 밑면이 블록의 밑면보다 크고 
        # 탑을 쌓았을 때 높이가 최대 탑 높이보다 높으면
        if ta > ba and bh + th > h_max:
            # 최대 탑 높이를 바꾸고
            h_max = bh + th
            # 쌓은 블록 번호를 해당 탑의 쌓은 블록 번호로 바꾼다.
            n_max = tns[:]
    # 쌓은 블록 번호에 현재 쌓은 블록의 번호를 추가하고
    n_max.append(bn)
    # 최대 높이, 현재 블록의 밑면과 함께 탑으로 추가한다.
    T.append((h_max, ba, n_max))

# 최대 높이인 탑의 쌓은 블록 번호를 가져온다.
res = max(T)[-1]
print(len(res))
print("\n".join(map(str, res[::-1])))

아이디어 & 풀이

비교 기준이 되는 ‘넓이’, ‘무게’ 중 하나를 선택해서 큰 것부터 순회하면서 쌓은 탑을 리스트 T에 추가한다.

  • 이 풀이에서는 ‘무게’를 기준으로 진행한다.

탑(i.e., T의 원소)은 (총 높이, 최소 밑면, [쌓은 블록 번호])로 구성한다.

  • max 사용 시 정렬이 바로 되도록 총 높이를 첫 원소로 둔다.
  • 최소 밑면은 블록을 쌓을지를 결정하는 기준이 된다.
  • 마지막에 쌓은 블록의 번호를 모두 출력해야 하므로 쌓은 블록 번호는 지금까지 쌓은 블록 번호의 리스트로 구성한다.

우선 무게가 큰 순서대로 블록을 순회하면서 해당 블록을 지금까지 쌓은 탑(i.e., T의 원소들) 위에 쌓을 수 있는지 확인한다.

  • 즉, 주어진 블록(쌓을 블록)에 대해 T의 원소를 순회하면서
  • 해당 블록의 밑면 값과 탑의 최소 밑면 값을 비교해서 쌓을 수 있는지 확인한다.
  • 무게가 큰 순서대로 순회하기 때문에 당연히 현재 받아온 블록을 이전까지 쌓은 탑에 쌓을 수 있다.

블록을 쌓아서 탑을 만드는 과정은 다음과 같다.

  • 해당 블록을 어떤 탑에도 쌓을 수 없다면 현재 블록 자체가 탑이 되므로 현재 블록을 탑으로 만들어 T에 추가한다.
  • 블록을 쌓을 수 있다면 쌓을 수 있는 탑 중 최대 높이의 탑 위에 그 블록을 추가한 뒤 T에 추가한다.
    • 높이는 기존 블록의 높이에 쌓는 블록의 높이를 합한 값이고
    • 최소 밑면은 현재 쌓는 블록의 밑면 값이 된다.
    • 마지막으로 해당 탑의 쌓은 블록 리스트에 현재 번호를 추가한다.
  • 무게가 큰 순서대로 순회하기 때문에 이미 쌓인 탑 중간에 블록을 끼워 넣을 수 없고, 블록을 위에 쌓는 것만 고려하면 된다.

디버그

  • T에 탑을 추가할 때 쌓은 블록 번호를 n_max.append(bn) 으로 입력했는데 결과가 None으로 떠서 틀렸다.
    • T에 추가하기 전에 우선 n_max에 먼저 append 해서 해결하긴 했는데 왜 그런지 알아봐야 할 것 같다.
  • n_max = tns로 작성했더니 n_maxtns를 ‘참조’하게 되면서 n_max를 수정할 때 tns도 같이 수정됐다.

2022.04.05

메모리 시간 코드 길이
11228 KB 192 ms 1115 B
단계 시작 시각 끝난 시각 걸린 시간
문제 이해 03:35:59 03:38:12
풀이 생각 11:06:26 11:21:49
코딩 1 11:21:51 11:52:25
코딩 2 15:26:44 15:55:56
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().trim().split("\n");

const [N, ...lines] = input;
const bricks = lines
    .map((brick, i) => {
        const indexedBrick = brick.split(" ").map((n) => parseInt(n));
        indexedBrick.push(i + 1);
        return indexedBrick;
    })
    .sort((a, b) => b[0] - a[0]);

function main() {
    const height = bricks.map((brick) => brick[1]);
    const stack = bricks.map((brick) => [brick[3]]);

    // 쌓는 벽돌
    bricks.forEach((brick, i) => {
        const [A, H, W, n] = brick;
        // 쌓여있는 벽돌
        let maxH = 0;
        let maxStack = [];
        for (let j = 0; j < i; j += 1) {
            const [Aj, _, Wj] = bricks[j];
            const Hj = height[j];

            if (W < Wj && Hj > maxH) {
                maxH = Hj;
                maxStack = stack[j];
            }
        }
        height[i] += maxH;
        stack[i].push(...maxStack);
    });

    const resStack = stack[height.indexOf(Math.max(...height))];

    console.log(resStack.length);
    console.log(resStack.join("\n"));
}

main();

참고 답안 1

B = sorted(
    [list(map(int, input().split())) + [i + 1] for i in range(int(input()))],
    reverse=True,
)

I = [[b[3]] for b in B]
H = [0 for _ in range(101)]

for i in range(len(B)):
    # H의 초깃값을 해당 블록의 높이로 지정한다.
    H[i] = B[i][1]
    # 현재 블록(i)의 이전 블록(j)들에 대해
    # (현재 블록보다 무게가 큰 블록)
    for j in range(i):
        # j번째 블록의 밑면이 i번째보다 크고
        # i번째 탑의 높이보다 j번째 탑의 높이에 i번째 블록의 높이를 합한 것이 더 크면
        if B[i][2] < B[j][2] and H[i] < H[j] + B[i][1]:
            # i번째 탑의 높이를 
            # j번째 탑의 높이에 i번째 블록의 높이를 더한 값으로 바꾸고.
            H[i] = H[j] + B[i][1]
            # i번째 탑의 블록 목록을
            # j번째 탑의 블록 목록에 i번째 블록을 추가한 것으로 바꾼다.
            I[i] = [B[i][3]] + I[j]

# 최대 높이를 갖는 탑의 블록 목록을 가져온다.
res = I[H.index(max(H))]
print(len(res))
[print(i) for i in res]

아이디어 & 풀이

전체적인 진행은 위의 풀이와 유사하지만, 탑의 전체 정보를 하나의 리스트로 관리하는 것이 아니라 필요한 것만 추려서 별개의 리스트로 관리한다.

  • 탑의 높이를 리스트 H
    • 각 인덱스 번째 값은 해당 인덱스 번째 블록이 가장 아래 있는 탑의 높이이다.
  • 그 탑을 이루고 있는 블록의 번호들을 리스트 I로 관리한다.

참고 답안 2

import sys
input = sys.stdin.readline

N = int(input())
B = [(0, 0, 0, 0)]
for i in range(1, N + 1):
    B.append((i, *map(int, input().split())))
# 블록을 무게에 대해 오름차순으로 정렬한다.
B.sort(key=lambda x: x[3])

# 탑의 높이를 담을 리스트
H = [0] * (N + 1)

# 각 블록을 순회하면서
for i in range(1, N + 1):
    # 빈 블록부터 해당 블록의 이전 블록들에 대해
    # (현재 블록보다 가벼운 블록)
    for j in range(0, i):
        # i번째 블록의 밑면이 j번째 블록보다 크면
        if B[i][1] > B[j][1]:
            # i번째 탑의 높이를
            # i번째 탑의 높이와 j번째 탑의 높이에 i번째 블록의 높이를 합한 것 중
            # 더 큰 값으로 지정한다.
            H[i] = max(H[i], H[j] + B[i][2])

# 탑의 최대 높이를 구하고
max_h = max(H)
# 그 탑의 번호를 구한 뒤
idx = H.index(max_h)
result = []

# 최대 높이 탑의 이전 탑들을 순회하면서
while idx != 0:
    # 해당 탑의 높이가 최댓값과 같으면
    if max_h == H[idx]:
        # 결과 리스트에 블록의 번호를 추가하고
        result.append(B[idx][0])
        # 최대 높이를 그 값에서 해당 블록의 높이를 뺀 값으로 바꾼다. 
        max_h -= B[idx][2]
    idx -= 1

print(len(result))
[print(i) for i in result[::-1]]

아이디어 & 풀이

탑의 높이만 리스트 H로 관리하고 최대 높이의 탑에서부터 역추적으로 그 위에 쌓인 탑의 번호를 알아낸다.

  • 각 인덱스 번째 값은 해당 인덱스 번째 블록이 가장 아래 있는 탑의 높이이다.
  • 블록 정렬을 위의 두 풀이와 반대로 했다는 점에 유의하자.