BOJ_1325 효율적인 해킹
- TOC {:toc}
이 글은 백준 온라인 저지의 1325번 문제를 풀이한 것을 모아놓은 글입니다.
일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.
2021.07.03 (Python)
| 메모리 | 시간 | 코드 길이 |
|---|---|---|
| 227688 KB | 11348 ms | 669 B |
| 단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
|---|---|---|---|
| 문제 이해 | 11:17:14 | 11:19:59 | |
| 풀이 생각 1 | 11:20:09 | 11:34:10 | |
| 코딩 1 | 11:35:36 | 11:43:08 | |
| 디버깅 1 | 11:45:07 | 12:07:46 | |
| 풀이 생각 2 | 12:10:50 | 12:15:23 | |
| 코딩 2 | 14:33:15 | 14:56:29 |
import sys
input = sys.stdin.readline
def dfs(root):
visited = [False] * (N + 1)
stack = [root]
count = 0
while stack:
n = stack.pop()
if not visited[n]:
visited[n] = True
count += 1
stack += hack[n]
return count
N, M = map(int, input().split())
hack = [[] for _ in range(N + 1)]
for _ in range(M):
A, B = map(int, input().split())
hack[B].append(A)
max_count = -1
res = []
for i in range(1, N + 1):
c = dfs(i)
if c > max_count:
max_count = c
res = [i]
elif c == max_count:
res.append(i)
print(*res)
아이디어 & 풀이
Python으로 풀 경우 대부분 시간 초과가 나는 것 같다. Pypy3 로 푸는 것을 권장한다.
일반적인 dfs, bfs 문제와 다른 점은 인접 노드가 단방향으로 연결되어있다는 점이다.
A가B를 신뢰할 때B만이B에서A로 접근할 수 있다.- 노드 사이의 연결 관계를 나타내는
hack리스트를 구성할 때B의 리스트에만A가 포함되도록 해야 한다.
N개의 노드를 순회하면서 그 노드에 연결된 다른 노드의 수를 구한다.
- 각 노드를 루트로 bfs나 dfs로 탐색하고 탐색 과정에서 노드를 방문할 때마다
count를 증가시킨 뒤count를 반환한다. count의 최댓값과 같은 값을 갖는 노드 번호를res에 추가한 뒤 출력한다.- 노드를 오름차순으로 탐색하므로 별도로 정렬은 하지 않아도 된다.
디버그
dfs의 인자로visited를 전달해 사용하면 해당 대상을 그대로 보낸 거라 함수 안에서 수정을 하면 전달한visited자체가 수정된다.dfs를 수행할 때마다 새로운visited가 필요하기 때문에dfs안에서visited를 초기화해 생성한다.
미해결
모든 노드에 대해 그래프를 매번 탐색하는 게 시간이 오래 걸릴 것 같아 어떤 컴퓨터도 신뢰하지 않는 컴퓨터만 순회하도록 작성했었는데 틀렸다.
- 자신(e.g.,
A)이 어떤 컴퓨터(e.g.,B를 신뢰하고 있으면B가A보다 더 많은 컴퓨터를 해결할 수 있다고 생각했다. trusted리스트를 모두False로 초기화한 뒤A, B를 입력받을 때마다trusted[A] = True로 변경한 뒤not trusted[i]인 경우에만dfs(i)를 실행했다.not trusted[i]조건만 없애면 정답으로 처리되는 걸 보니 답으로 들어가야 할 경우를 걸러내는 것 같은데 생각해 봐야겠다.
각 노드에서 그래프를 탐색할 때, 반복적으로 거치는 부분들이 많아서 해킹할 수 있는 컴퓨터 수를 각 노드를 거칠 때마다 count에 저장해서 사용하는 방식으로 바꿔보려 했었는데 메모리 초과가 나서 하지 못했다.
- 해당 컴퓨터를 신뢰하는 다른 컴퓨터가 없으면
count값은 1이다. - 이미
count값이 존재하면 그 값을 사용한다. - 신뢰받는 컴퓨터가 있으면 그 컴퓨터들의
count합에 자기 자신인 1을 더한다. count의 최댓값을 구하고 그 값을 갖는 원소를 정렬해서 출력한다.
import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline
def dfs(n):
if not trusted[n]:
count[n] = 1
return 1
elif count[n]:
return count[n]
else:
count[n] = sum([dfs(x) for x in trusted[n]]) + 1
return count[n]
N, M = map(int, input().split())
trusted = [[] for _ in range(N + 1)]
count = [0] * (N + 1)
res = []
for _ in range(M):
A, B = map(int, input().split())
trusted[B].append(A)
for i in range(1, N + 1):
if not count[i]:
dfs(i)
max_count = max(count)
print(*sorted([i for i, x in enumerate(count) if x == max_count]))
시간이 굉장히 짧게 걸린 풀이들이 있는데 그거 보고 파악해보기.
2022.04.05 (JS)
| 메모리 | 시간 | 코드 길이 |
|---|---|---|
| 54348 KB | 7392 ms | 1074 B |
| 단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
|---|---|---|---|
| 문제 이해 | 00:54:19 | 00:55:46 | |
| 풀이 생각 | 00:55:47 | 00:57:07 | |
| 코딩 | 00:57:09 | 01:20:58 | |
| 디버깅 | 01:21:46 | 02:05:28 |
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().trim().split("\n");
function dfs(i) {
let count = 0;
const visited = Array(N + 1).fill(false);
const stack = [i];
visited[i] = true;
while (stack.length >= 1) {
const n = stack.pop();
const len = edges[n].length;
for (let i = 0; i < len; i += 1) {
const m = edges[n][i];
if (!visited[m]) {
count += 1;
visited[m] = true;
stack.push(m);
}
}
}
hacked[i] = count;
}
const [NM, ...lines] = input;
const [N, M] = NM.split(" ").map((n) => parseInt(n));
const edges = Array(N + 1)
.fill()
.map((i) => []);
const hacked = Array(N + 1).fill(0);
const res = [];
lines.forEach((line) => {
const [A, B] = line.split(" ").map((n) => parseInt(n));
edges[B].push(A);
});
for (let i = 1; i <= N; i++) {
dfs(i);
}
const max = Math.max(...hacked);
for (let i = 0; i <= N; i++) {
if (hacked[i] === max) res.push(i);
}
console.log(...res);
디버그
- 시간초과가 나길래 통과한 답들이랑 비교해봤는데 가장 크게 영향을 미친것은
dfs내에서for문 대신에forEach를 사용해서 순회한 것이었다. 그것만 바꿨을 때 통과하던 코드가 시간초과로 통과하지 못함. - 그 외의 것은 시간에 영향이 있긴 했지만 크지 않았다.
- 입력을 받을 때
for문 대신forEach를 사용 - 결과 문자열을 생성할 때
hacked를 순회하면서res를 문자열로 잇는 대신res를 배열로 해서 원소를push한 다음에spread문법을 이용해서 출력 dfs함수 내에서hacked값을count로 바로 지정하는 대신count값을 반환해서 함수 밖에서hacked값을 지정하는 등
- 입력을 받을 때
-
ps-python ps-js draft