BOJ_1987 알파벳
- TOC {:toc}
이 글은 백준 온라인 저지의 1987번 문제를 풀이한 것을 모아놓은 글입니다.
일종의 연습 기록이며 제가 정답을 받은 코드와 참고할만한 다른 코드를 같이 기록합니다. 필요한 경우 코드에 대한 해설을 기록합니다만 코드는 통과했어도 해설은 틀릴 수 있기 때문에 가볍게 참고해주시길 부탁드립니다. 피드백은 편하신 방법으로 자유롭게 주시면 감사하겠습니다.
2021.07.22 (Python)
메모리 | 시간 | 코드 길이 |
---|---|---|
194376 KB | 7324 ms | 594 B |
단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
---|---|---|---|
문제 이해 | 20:04:40 | 20:09:13 | |
풀이 생각 | 20:37:38 | 21:20:27 | |
코딩 | 21:20:35 | 21:51:13 | |
디버깅 | 21:51:38 | 22:14:44 |
import sys
input = lambda: sys.stdin.readline().rstrip()
R, C = map(int, input().split())
board = [list(map(lambda x: ord(x) - 65, input())) for _ in range(R)]
visited = [0] * 26
visited[board[0][0]] = 1
dx = [0, 1, 0, -1]
dy = [1, 0, -1, 0]
def dfs(x, y, l):
global max_len
max_len = max(l, max_len)
for k in range(4):
i = x + dx[k]
j = y + dy[k]
if 0 <= i < R and 0 <= j < C and not visited[board[i][j]]:
visited[board[i][j]] = 1
dfs(i, j, l + 1)
visited[board[i][j]] = 0
max_len = 1
dfs(0, 0, 1)
print(max_len)
아이디어 & 풀이
백 트래킹을 이용하는 문제이다.
입력받은 보드를 이차원 리스트로 구성한 뒤 dfs를 이용해 현재 보드의 상하좌우를 돌면서 방문한다.
- 사용한 문자를 확인할 때, 딕셔너리나 리스트 안에 해당 문자가 있는지 확인하는 것보다 참/거짓을 확인하는 것이 시간이 적게 소요된다.
visited
같은True
/False
리스트를 구성하려면 (e.g.,visited[A] == True
) 입력받은 알파벳이 각 인덱스가 되어야 하고 이를 위해서 A ~ Z까지의 문자를 0 ~ 25의 숫자로 치환해주는 것이 효율적이다.lambda
를 이용해서 아스키코드로 치환한 뒤A
의 값인 65를 빼준 값으로 매핑해 보드를 구성한다.
해당 알파벳을 이전에 이미 사용했으면 더는 탐색하지 않고 drop 한다.
디버그
- 처음에 문자 그대로 입력받아서 사용 여부를 확인했더니 시간초과가 났다.
- 위의 풀이처럼 입력받은 문자를 숫자로 치환해서 검사를 진행했다.
- 입력받은 문자열의 문자 각각을 리스트로 만들기 때문에
readline()
을 사용할 경우rstrip()
해줘야 한다. - 상하좌우 방문을
for i, j in ((x + 1, y), (x, y + 1), ...)
의 꼴로 했는데 이 역시 시간초과가 나서 위와 같이 바꿔주었다.
피드백
- 이번 문제에서는 왜 dfs를 스택으로 시도하지 않았을까.
2022.04.08 (JS)
메모리 | 시간 | 코드 길이 |
---|---|---|
11092 KB | 1652 ms | 978 B |
단계 | 시작 시각 | 끝난 시각 | 걸린 시간 |
---|---|---|---|
문제 이해 | 00:19:03 | 00:20:22 | |
풀이 생각 | 00:20:23 | 00:22:39 | |
코딩 | 00:22:40 | 01:08:06 | |
디버깅 | 11:13:22 | 11:43:19 |
const fs = require("fs");
const input = fs.readFileSync('/dev/stdin').toString().trim().split("\n");
function main() {
function dfs(x, y, count) {
const dx = [1, 0, -1, 0];
const dy = [0, -1, 0, 1];
for (let i = 0; i < 4; i += 1) {
const nx = x + dx[i];
const ny = y + dy[i];
if (0 <= nx && nx < C && 0 <= ny && ny < R && !isVisited[board[ny][nx]]) {
isVisited[board[ny][nx]] = true;
dfs(nx, ny, count + 1);
isVisited[board[ny][nx]] = false;
} else {
max = Math.max(count, max);
}
}
}
const isVisited = Array(26).fill(false);
let max = 0;
isVisited[board[0][0]] = true;
dfs(0, 0, 1);
return max;
}
const [RC, ...lines] = input;
const [R, C] = RC.split(" ").map((n) => parseInt(n));
const board = lines.map((line) => line.split("").map((c) => c.charCodeAt(0) - 65));
console.log(main());
디버그
- 일반적인 dfs는
isVisited
를 모든 경로에서 공유하기 때문에 생성되는 경로마다 별도로isVisited
를 관리할 방법을 찾다가 지금까지 지나온 문자열을 인자로 넘겨서 해당 문자열에 현재 값이 있는지 확인하는 방식으로 작성했는데 결국 마지막쯤에서 시간초과됐다. isVisited
를 관리하는게 나을 것 같았는데 방법을 제대로 못 찾았다.- 핵심은 dfs 함수로 전달하기 전에 해당 문자의
isVisited
를true
으로 바꾸고 전달해서 실행한 다음에 다시false
로 돌려놓는 것
피드백
isVisited
를 배열로 관리하려다 보니board
를 순회할 때마다 이 값을 숫자로 변경하면 시간 낭비가 심할 것 같아서isVisited
를 문자가 key인 객체로 만들었는데,board
의 값을 숫자로 바꾸는게 훨씬 나은 방법이었다.
참고 답안 0
import sys
input = lambda: sys.stdin.readline().strip()
r, c = map(int, input().split())
a = [list(map(lambda x: ord(x) - 65, input())) for i in range(r)]
ch = [0] * 26
dx = [-1, 0, 1, 0]
dy = [0, 1, 0, -1]
def dfs(x, y, z):
global answer
answer = max(answer, z)
for i in range(4):
nx = x + dx[i]
ny = y + dy[i]
if 0 <= nx < r and 0 <= ny < c and ch[a[nx][ny]] == 0:
ch[a[nx][ny]] = 1
dfs(nx, ny, z + 1)
ch[a[nx][ny]] = 0
answer = 1
ch[a[0][0]] = 1
dfs(0, 0, answer)
print(answer)
아이디어 & 풀이
풀이 방식은 동일한데 위의 풀이가 가장 시간이 적게 걸린다.
참고 답안 1
import sys
input = lambda: sys.stdin.readline().rstrip()
def bfs():
mx = 0
queue = set([(0, 0, board[0][0])])
while queue:
x, y, word = queue.pop()
mx = max(mx, len(word))
if mx == 26:
return 26
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < R and 0 <= ny < C and not board[nx][ny] in word:
queue.add((nx, ny, word + board[nx][ny]))
return mx
R, C = map(int, input().split())
board = [list(input()) for _ in range(R)]
dx = (0, 1, 0, -1)
dy = (1, 0, -1, 0)
print(bfs())
아이디어 & 풀이
bfs에 재귀 대신 스택을 이용한다.
- 스택에 리스트 대신 집합 자료구조를 이용해 중복 방문을 최소화한다.
- 지금까지 만든 단어를 함수의 인자를 보내 이 안에 현재 받은 문자가 있는지 확인한다.
-
ps-python ps-js