BOJ_2655 가장높은탑쌓기
- 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_max
가tns
를 ‘참조’하게 되면서n_max
를 수정할 때tns
도 같이 수정됐다.- 탑을 쌓을 때 블럭을 추가로 쌓은 탑을 별도로
T
에 추가하기 때문에 이전에 존재하던 탑은 바뀌는 것 없이 남아있어야 하는데 위의 이유로 존재하던 탑의 쌓은 블록 번호가 수정돼서 틀렸다. n_max
에tns
를 복사하는 것으로 해결했다. 원소가 단순 인덱스값이라 얕은 복사로도 충분했다.- 12. 얕은 복사(shallow copy)와 깊은 복사(deep copy) by 파이썬 - 기본을 갈고 닦자!
- [Python] 얕은복사 깊은복사 ([:], copy(),deepcopy) by aonee
- copy — Shallow and deep copy operations by Python Documentation
- 탑을 쌓을 때 블럭을 추가로 쌓은 탑을 별도로
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
로 관리하고 최대 높이의 탑에서부터 역추적으로 그 위에 쌓인 탑의 번호를 알아낸다.
- 각 인덱스 번째 값은 해당 인덱스 번째 블록이 가장 아래 있는 탑의 높이이다.
- 블록 정렬을 위의 두 풀이와 반대로 했다는 점에 유의하자.
-
ps-python ps-js