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