문제
쿠키런은 데브시스터즈에서 제작한 모바일 러닝 액션 게임이다. 마녀의 오븐에서 탈출한 쿠키들과 함께 모험을 떠나는 게임으로, 점프와 슬라이드 2가지 버튼만으로 손쉽게 플레이할 수 있는 것이 특징이다.
연세대학교를 졸업한 김강산 선배님이 데브시스터즈에 취직하면서 주변 사람들에게 쿠키런을 전파시켰다. 하지만 게임을 전파하던 중에 쿠키들에게 신체적으로 이상이 생기는 것을 발견하였다. 팔, 다리 길이가 임의적으로 변한 것이다. 때문에 긴급하게 각 쿠키들의 신체들을 측정하려고 한다.
쿠키들은 신체를 측정하기 위해서 한 변의 길이가 N인 정사각형 판 위에 누워있으며, 어느 신체 부위도 판 밖으로 벗어나지 않는다. 판의 x번째 행, y번째 열에 위치한 곳을 (x, y)로 지칭한다. 판의 맨 왼쪽 위 칸을 (1, 1), 오른쪽 아래 칸을 (N, N)으로 나타낼 수 있다.

그림과 같이 쿠키의 신체는 머리, 심장, 허리, 그리고 좌우 팔, 다리로 구성되어 있다. 그림에서 빨간 곳으로 칠해진 부분이 심장이다. 머리는 심장 바로 윗 칸에 1칸 크기로 있다. 왼쪽 팔은 심장 바로 왼쪽에 붙어있고 왼쪽으로 뻗어 있으며, 오른쪽 팔은 심장 바로 오른쪽에 붙어있고 오른쪽으로 뻗어있다. 허리는 심장의 바로 아래 쪽에 붙어있고 아래 쪽으로 뻗어 있다. 왼쪽 다리는 허리의 왼쪽 아래에, 오른쪽 다리는 허리의 오른쪽 아래에 바로 붙어있고, 각 다리들은 전부 아래쪽으로 뻗어 있다. 각 신체 부위들은 절대로 끊겨있지 않으며 굽혀진 곳도 없다. 또한, 허리, 팔, 다리의 길이는 1 이상이며, 너비는 무조건 1이다.
쿠키의 신체가 주어졌을 때 심장의 위치와 팔, 다리, 허리의 길이를 구하여라.
풀이
sys.stdin.read().splitlines()으로 \n으로 구분하여 한 번에 모든 문자열을 읽어온 뒤, 반복문에서 한 줄씩 읽어와 직관적인 조건문으로 각 신체부위 값이 계산되도록 하였다. 몸의 길이와 심장의 좌표는 list로 사전에 정의해 두었다. 또 쿠키가 시작되었는지 확인하는 isHead 플래그 변수를 사용하였다.
첫 번째 코드
import sys
data = sys.stdin.read().splitlines()
n = int(data[0])
body = [0, 0, 0, 0, 0] # 왼팔 오른팔 허리 왼다리 오른다리
heart = [0, 0] # 심장 위치
isHead = False
for s in data[1:]:
if s.count("*") == 1 and isHead == False: # 머리 발견
isHead = True
heart[1] = s.index("*") # 머리 y좌표 저장
elif s.count("*") >= 3: # 팔 발견
body[0] = s[: heart[1]].count("*")
body[1] = s[heart[1] + 1 :].count("*")
elif s.count("*") == 1 and isHead == True and heart[1] == s.index("*"): # 허리 발견
body[2] += 1
elif s.count("*") == 2:
body[3] += 1
body[4] += 1
elif s.count("*") == 1 and isHead == True and heart[1] != s.index("*"): # 다리 발견
if s.index("*") < heart[1]:
body[3] += 1
else:
body[4] += 1
if isHead == False:
heart[0] += 1
print(f"{heart[0]+2} {heart[1]+1}")
print(f"{body[0]} {body[1]} {body[2]} {body[3]} {body[4]}")
반복적으로 사용되는 변수, 함수들 때문에 코드가 복잡해 보인다.
기존 로직은 유지하되 약간 간략화하였다.
1. print(*list) 사용
: f-string을 사용해 list의 모든 값을 하나하나 써두는 대신 print(*body)를 사용하여 한 번에 list의 모든 값이 출력되도록 하였다.
2. boolean 변수 조건문 활용 간략화
: boolaen 변수인 isHead의 경우 조건문에서 매번 ==True/False를 쓰는 대신 간단히 not을 붙이는 방식으로 수정하였다.
3. s.count("*") 값 변수에 저장해두고 사용
: s.count("*")를 매번 호출하는 대신 star_count라는 변수에 저장해두고 사용하도록 수정하였다.
최종 코드
import sys
data = sys.stdin.read().splitlines()
n = int(data[0])
body = [0, 0, 0, 0, 0] # 왼팔 오른팔 허리 왼다리 오른다리
heart = [0, 0] # 심장 위치
isHead = False
for s in data[1:]:
star_count = s.count("*")
if star_count == 1 and not isHead: # 머리 발견
isHead = True
heart[1] = s.index("*") # 머리 y좌표 저장
elif star_count >= 3: # 팔 발견
body[0] = s[: heart[1]].count("*")
body[1] = s[heart[1] + 1 :].count("*")
elif star_count == 1 and isHead and heart[1] == s.index("*"): # 허리 발견
body[2] += 1
elif star_count == 2:
body[3] += 1
body[4] += 1
elif star_count == 1 and isHead and heart[1] != s.index("*"): # 다리 발견
if s.index("*") < heart[1]:
body[3] += 1
else:
body[4] += 1
if isHead == False:
heart[0] += 1
print(f"{heart[0]+2} {heart[1]+1}")
print(*body)
약간이지만 코드가 개선되었다.
사실 원래 가독성을 더 높이기 위해 중복되는 조건들이 있는 조건문들은 최대한 한 if문 안에 집어넣고 중첩 if문들로 구성하였었다. 가독성이 많이 개선되어 좋은 코드라고 생각했지만, 실행 결과 코드 길이는 줄어들었어도 실행 시간이 오히려 32ms에서 40ms로 늘어났다. 지나치게 가독성만 신경 쓰느라 불필요하게 중첩 if문을 늘린 것이 원인이었다. 결과적으로 기존의 if-else 로직은 유지하되 세세한 부분만 수정하였다.
가독성과 효율성, 둘 다 치우치지 않고 챙기는 것이 중요하다는 것을 느꼈다.
배운 것
- print(*list): list의 원소들이 공백으로 구분되어 한 줄에 출력된다.
'[Python] 백준 알고리즘 공부' 카테고리의 다른 글
| [Python] 1244. 스위치 켜고 끄기 (0) | 2026.02.20 |
|---|---|
| [Python] 1205. 등수 구하기 (0) | 2026.02.20 |
| [Phython] 25757. 임스와 함께하는 미니게임 (2) | 2026.02.18 |
| [Phython] 4659. 비밀번호 발음하기 (0) | 2026.02.18 |
| [Python] 7568. 덩치 (0) | 2026.02.16 |