1. DFS/BFS 문제: 거리두기 확인하기
실수 1. 함수 호출 인자 개수 불일치
잘못 쓴 코드:
dfs((i, j, place, 1))
함수 정의:
def dfs(x, y, place, depth):
dfs는 인자 4개를 받는데, 튜플 1개를 넘긴 형태였다.
수정:
dfs(i, j, place, 1)
또는 더 깔끔하게는 depth를 0부터 시작한다.
dfs(i, j, place, 0)
실수 2. 재귀 결과를 부모 함수에서 받지 않음
잘못 쓴 코드:
dfs(nx, ny, place, depth + 1)
이렇게 하면 안쪽 DFS에서 False를 반환해도 바깥 DFS가 그 값을 모른다.
수정:
if dfs(nx, ny, place, depth + 1) == False:
return False
DFS에서 위반을 발견했으면 그 결과를 끝까지 전달해야 한다.
기억할 것:
재귀 안에서 찾은 False는 부모 호출까지 return 해야 한다.
실수 3. visited를 전체 place에서 한 번만 사용함
잘못된 흐름:
visited = [[False] * m for _ in range(n)]
for 모든 P:
dfs()
이렇게 하면 첫 번째 P에서 방문한 좌표가 두 번째 P 탐색에도 영향을 줄 수 있다.
수정:
for i in range(n):
for j in range(m):
if place[i][j] == 'P':
visited = [[False] * m for _ in range(n)]
visited[i][j] = True
dfs(i, j, place, 0)
이 문제는 각 P를 기준으로 거리 2까지만 탐색하는 문제이므로, visited는 P마다 새로 만드는 게 안전하다.
실수 4. 파티션을 변수로 기억하려고 함
처음에는 이런 식으로 생각했다.
if place[nx][ny] == 'X':
partition = True
하지만 이 문제에서는 그럴 필요가 없다.
X는 벽이다. 만나면 그 방향 탐색을 끊으면 된다.
if place[nx][ny] == 'X':
continue
기억할 것:
격자 탐색에서 벽은 상태로 들고 가지 말고, 보통 continue로 막는다.
실수 5. 문자 대소문자 주의
문제의 값은 보통 이렇게 주어진다.
'P', 'O', 'X'
잘못 쓰면 오류 또는 오답이 난다.
if place[i][j] == p: # p라는 변수 없음
if place[nx][ny] == 'x': # 문제에서는 'X'
수정:
if place[i][j] == 'P':
if place[nx][ny] == 'X':
실수 6. 거리 2 케이스를 놓침
꼭 체크해야 하는 케이스:
P O P
맨해튼 거리 2이고 중간이 O라서 위반이다.
대각선도 체크해야 한다.
P O
O P
이것도 맨해튼 거리 2이고 파티션이 없으면 위반이다.
시험에서 직접 만들어볼 테스트:
# 바로 옆 P: 위반
["PPOOO",
"OOOOO",
"OOOOO",
"OOOOO",
"OOOOO"]
# 거리 2, 중간 O: 위반
["POPOO",
"OOOOO",
"OOOOO",
"OOOOO",
"OOOOO"]
# 거리 2, 중간 X: 통과
["PXPOO",
"OOOOO",
"OOOOO",
"OOOOO",
"OOOOO"]
# 대각선 거리 2: 위반
["POOOO",
"OPOOO",
"OOOOO",
"OOOOO",
"OOOOO"]
2. 구현 문제: 주차 요금 계산
실수 1. defaultdict key 정렬 문법
잘못 쓴 코드:
record_dict = sorted(record_dict, lambda key=x:x[0])
문제점:
lambda key=x:x[0]
이 문법이 틀렸다.
수정:
record_dict = dict(sorted(record_dict.items(), key=lambda x: x[0]))
기억할 것:
sorted(딕셔너리.items(), key=lambda x: x[0])
- x[0]: key 기준 정렬
- x[1]: value 기준 정렬
실수 2. dict_items는 바로 인덱싱 불가
잘못 쓴 코드:
record_dict.items()[0]
record_dict.items()는 리스트가 아니라 dict_items 객체라서 바로 [0] 접근이 안 된다.
수정:
list(record_dict.items())[0]
다만 보통은 이렇게 순회하는 게 맞다.
for car_num, time_records in record_dict.items():
print(car_num, time_records)
실수 3. 문자열 시간끼리는 뺄 수 없음
잘못된 생각:
"23:59" - "16:00"
문자열끼리는 뺄 수 없다.
시간은 분으로 바꿔야 한다.
def time_to_minute(time):
h, m = time.split(":")
return int(h) * 60 + int(m)
예시:
time_to_minute("16:00") # 960
time_to_minute("18:00") # 1080
실수 4. 출차 기록이 없는 경우 처리
입차만 있고 출차가 없으면 23:59에 출차한 것으로 본다.
잘못된 접근:
time_to_minute(time_record[-1])
record_dict[num].remove(-1)
문제점:
- time_to_minute(time_record[-1])만 하면 결과를 저장하지 않는다.
- remove(-1)은 마지막 요소 삭제가 아니라 값이 -1인 요소 삭제다.
- 이 문제에서는 삭제보다 "23:59"를 추가하는 게 훨씬 편하다.
수정:
if len(time_records) % 2 == 1:
time_records.append("23:59")
기억할 것:
입출차 기록 개수가 홀수면 출차가 없는 것 → "23:59" 추가
실수 5. 입출차가 여러 번일 수 있음
잘못 쓴 코드:
for _ in range(len(record_dict[num])-1):
time += time_to_minute(record_dict[num][1]) - time_to_minute(record_dict[num][0])
이러면 항상 첫 번째 출차 - 첫 번째 입차만 반복해서 더한다.
예를 들어:
['05:34', '07:59', '22:59', '23:00']
정답 계산:
07:59 - 05:34
+
23:00 - 22:59
수정:
total_time = 0
for i in range(0, len(time_records), 2):
total_time += time_to_minute(time_records[i + 1]) - time_to_minute(time_records[i])
기억할 것:
입차/출차는 2개씩 묶어서 계산한다.
실수 6. 기본 시간 하드코딩
잘못 쓴 코드:
if time <= 180:
문제의 기본 시간은 fees[0]이다.
입력마다 달라질 수 있으므로 하드코딩하면 안 된다.
수정:
if total_time <= default_time:
실수 7. 추가 요금은 round()가 아니라 올림
잘못 쓴 코드:
round(default_fee + ((time-default_time)/p_time) * p_fee)
문제에서는 단위 시간으로 나누어떨어지지 않으면 올림해야 한다.
수정:
import math
extra_fee = math.ceil((total_time - default_time) / p_time) * p_fee
fee = default_fee + extra_fee
기억할 것:
요금 문제에서 "단위 시간마다" + "초과하면" → 대부분 ceil 사용
3. SQL 문제: 자동차 대여 기록에서 대여중 / 대여 가능 여부 구분하기
헷갈린 부분: GROUP BY를 왜 쓰는가?
차량 하나에 여러 대여 기록이 있을 수 있다.
예를 들어 한 차에 대해 이런 기록이 있을 수 있다.
대여중
대여 가능
대여 가능
그냥 조회하면 한 차량이 여러 줄로 나온다.
하지만 정답은 차량 하나당 한 줄이어야 한다.
그래서 CAR_ID 기준으로 묶어야 한다.
GROUP BY CAR_ID
핵심 조건
특정 날짜가 대여 기간 안에 포함되면 대여중이다.
'2022-10-16' BETWEEN START_DATE AND END_DATE
이 조건을 만족하는 기록이 하나라도 있으면 대여중이어야 한다.
그래서 MAX()를 활용한다.
CASE
WHEN MAX(CASE
WHEN '2022-10-16' BETWEEN START_DATE AND END_DATE
THEN 1
ELSE 0
END) = 1
THEN '대여중'
ELSE '대여 가능'
END
기억할 것:
하나라도 있으면 true → MAX(CASE WHEN 조건 THEN 1 ELSE 0 END)
전부 만족해야 true → MIN(CASE WHEN 조건 THEN 1 ELSE 0 END)
개수 세기 → SUM(CASE WHEN 조건 THEN 1 ELSE 0 END)
4. 오늘 반복된 문법 실수 모음
lambda 문법
잘못:
lambda key=x:x[0]
수정:
lambda x: x[0]
sorted 문법
잘못:
sorted(record_dict, lambda x: x[0])
수정:
sorted(record_dict.items(), key=lambda x: x[0])
함수 인자 전달
잘못:
dfs((i, j, place, 1))
수정:
dfs(i, j, place, 1)
리스트 마지막 요소 삭제
잘못:
arr.remove(-1)
수정:
arr.pop()
다만 주차 요금 문제에서는 삭제보다 추가가 낫다.
time_records.append("23:59")
딕셔너리 순회
추천 형태:
for key, value in record_dict.items():
print(key, value)
첫 번째 요소 접근:
list(record_dict.items())[0]
문자열 시간 계산
잘못:
"23:59" - "16:00"
수정:
time_to_minute("23:59") - time_to_minute("16:00")
5. 시험장에서 예외케이스 떠올리는 법
문제를 읽고 바로 이 순서로 체크한다.
1. 최소 케이스
입력이 하나만 있을 때도 되는가?
예:
records = ["16:00 3961 IN"]
출차가 없으므로 23:59 처리해야 한다.
2. 반복 케이스
같은 대상이 여러 번 등장하는가?
예:
['05:34', '07:59', '22:59', '23:00']
첫 번째 입출차만 계산하면 오답이다.
3. 조건이 바뀌는 경계
거리두기 문제:
거리 1 → 무조건 위반
거리 2 → 파티션 여부 확인
거리 3 → 상관 없음
주차 요금 문제:
누적 시간 <= 기본 시간 → 기본 요금
누적 시간 > 기본 시간 → 추가 요금
나누어떨어지지 않음 → 올림
4. 없을 때
- 출차 기록이 없을 때
- 대여중 기록이 없을 때
- 탐색 중 더 이상 갈 곳이 없을 때
이런 경우 기본값을 어떻게 처리할지 정해야 한다.
5. 반환값이 전달되는지
DFS에서 특히 중요하다.
if dfs(...) == False:
return False
이 패턴을 안 쓰면 안쪽에서 찾은 정답이나 실패가 바깥으로 전달되지 않는다.
6. 오늘 문제에서 가져갈 핵심 패턴
DFS 거리 제한 탐색
def dfs(x, y, depth):
if depth >= 2:
return True
for d in range(4):
nx = x + dx[d]
ny = y + dy[d]
if 범위 밖:
continue
if visited[nx][ny]:
continue
if board[nx][ny] == 'X':
continue
if board[nx][ny] == 'P':
return False
visited[nx][ny] = True
if dfs(nx, ny, depth + 1) == False:
return False
return True
딕셔너리 key 기준 정렬
record_dict = dict(sorted(record_dict.items(), key=lambda x: x[0]))
시간 문자열을 분으로 변환
def time_to_minute(time):
h, m = time.split(":")
return int(h) * 60 + int(m)
입출차 쌍 계산
if len(time_records) % 2 == 1:
time_records.append("23:59")
total_time = 0
for i in range(0, len(time_records), 2):
total_time += time_to_minute(time_records[i + 1]) - time_to_minute(time_records[i])
추가 요금 올림 계산
import math
if total_time <= default_time:
fee = default_fee
else:
extra_time = total_time - default_time
fee = default_fee + math.ceil(extra_time / unit_time) * unit_fee
7. 나의 오늘 실수 요약
오늘 실수는 크게 4가지다.
- 재귀 반환값을 부모 호출로 전달하지 않음
- 입출차 기록처럼 쌍으로 봐야 하는 데이터를 첫 번째 값만 반복 계산함
- 문자열 시간을 숫자로 변환하지 않고 계산하려 함
- 정렬, lambda, dict_items, remove/pop 같은 파이썬 기본 문법에서 실수가 남
1. 입력을 어떤 자료구조로 모을 것인가?
2. 같은 key가 여러 번 나오나?
3. 누적 계산인가, 한 번 계산인가?
4. 빠진 기록이나 기본값이 있나?
5. 정렬 기준은 key인가 value인가?
6. DFS/BFS라면 결과가 return으로 전달되는가?