프로그래머스 코딩테스트 1단계에 수록되어있는 카카오 인턴십 '키패드 누르기' 문제를 풀었습니다. 풀고 나서 코드가 좀 길다고 생각했는데 다른 사람들의 풀이를 보니 다들 비슷한 길이라서 안심이 되었습니다. (한심...ㅎ) 길다고 무조건 나쁜 코드, 짧다고 무조건 좋은 코드는 아니긴 합니다. 그래도 최대한 니트하고 간결하게 작성하고싶은 욕심이 드는 건 어쩔 수가 없네요 ㅎ_ㅎ (그럼에도 불구하고 제 코드는 항상 긴 편인거 같아요.)
저는 키패드에 좌표 개념을 도입하여 간단하게 문제를 풀어보았습니다. 문제 풀이 시간은 5분-10분정도로 그래도 최근에 연습했던 알고리즘 문제들에 비교하면 비교적 수월했던 문제였던 것 같습니다. 그럼 풀어보겠습니다!
문제 상황 : 이 전화 키패드에서 왼손과 오른손의 엄지손가락만을 이용해서 숫자만을 입력하려고 합니다. 맨 처음 왼손 엄지손가락은 * 키패드에 오른손 엄지손가락은 # 키패드 위치에서 시작하며, 엄지손가락을 사용하는 규칙은 다음과 같습니다. 1. 엄지손가락은 상하좌우 4가지 방향... (중략....) 자세한 문제 상황과 조건, 입출력, 테스트 케이스 등은 꼭 프로그래머스에서 확인해 보세요! https://school.programmers.co.kr/learn/courses/30/lessons/67256 |
1. 문제의 이해
먼저 정수로 이루어진 리스트 numbers가 인풋으로 주어집니다. 문제 조건을 보면, 정수가 1, 4, 7일 때에는 바로 왼손을, 3, 6, 9일때는 바로 오른손을 쓰면 되기 때문에 아무런 제약이 없어서 굉장히 쉽습니다. 우리가 고민해야 할 부분은 정수가 2, 5, 8, 0일 때입니다.
2, 5, 8, 0이 주어졌을 때 조건에 맞게 손가락을 움직이기 위해서는
1) 그 전의 손가락 위치를 알고 있어야 합니다.
2) 그 전의 손가락 위치와 2/5/8/0 사이의 거리를 숫자로 나타낼 수 있어야 합니다.
3) 왼손, 오른손과 2/5/8/0 사이의 거리를 비교할 수 있어야 합니다.
그럼 위 세 가지를 고민하면서 문제를 풀어보겠습니다.
2. 풀이
[1] 변수와 좌표 설정
def solution(numbers, hand):
answer = ""
current_left_thumb = (4, 1)
current_right_thumb = (4, 3)
저는 키패드 1이 있는 곳을 좌표 (1, 1)으로,
키패드 3이 있는 곳을 좌표 (1, 3)으로,
키패드 *이 있는 곳을 좌표 (4, 1)으로,
키패드 #이 있는 곳을 좌표 (4, 3)으로 놓았습니다.
첫줄을 파이썬 인덱싱과 같이 0부터 시작할까 하다가 보다 직관적으로 하기 위해 1부터 시작해줬는데, 0부터 시작해도 큰 상관은 없습니다.
current_left_thumb, current_right_thumb라는 변수명을 사용하여 왼손/오른손을 구분하여 현재 손가락이 어디 있는지를 좌표값으로 표현했습니다. 변수명이 조금 길고 번잡하긴 하지만 직관적으로 이해할 수 있어서 스스로 코드 이해하기가 편했습니다. 이 두 가지 변수는 앞으로 numbers에 주어진 정수값에 따라 손가락이 움직일 때 함께 업데이트가 되어 갈 예정입니다!
[2] 1/4/7, 3/6/9 처리
for number in numbers:
if number == 1:
answer += "L"
current_left_thumb = (1, 1)
elif number == 4:
answer += "L"
current_left_thumb = (2, 1)
elif number == 7:
answer += "L"
current_left_thumb = (3, 1)
elif number == 3:
answer += "R"
current_right_thumb = (1, 3)
elif number == 6:
answer += "R"
current_right_thumb = (2, 3)
elif number == 9:
answer += "R"
current_right_thumb = (3, 3)
다음으로 numbers 리스트의 값(value)를 가지고 하나씩 살펴보면서 이터레이션을 돌려줍니다. 리스트 이터레이션은 습관적으로 인덱스로 하게 되는데, 이번 문제에서는 굳이 인덱스로 조회할 필요가 없어서 편했습니다.
만약 number가 1/4/7중에 하나라면 answer 스트링에 L을 추가하고 해당 좌표값으로 왼손을 움직입니다(current_left_thumb 변수의 좌표를 업데이트합니다.). 만약 number 3/6/9중에 하나라면 answer 스트링에 R을 추가하고 해당 좌표값으로 오른손을 움직입니다(current_right_thumb 변수의 좌표를 업데이트합니다.).
이 부분의 문법이 어쩔수없이 위처럼 길어지게 되었는데, 어떻게 하면 더 짧게 쓸 수 있을까 잠깐 고민했어요. 하지만 지금 이대로도 아주 직관적이고 이해하기 편하기 때문에 굳이 줄이지 않아도 되겠다고 판단하고 다음 단계로 넘어갔습니다.
[3] 2/5/8/0 처리
elif number in [2, 5, 8, 0]:
if number == 2: x = 1
if number == 5: x = 2
if number == 8: x = 3
if number == 0: x = 4
다음으로 가장 중요한 2, 5, 8, 0입니다. 저는 2, 5, 8, 0이 모두 같은 세로줄에 있다는 점에 착안하여 아이디어를 얻어서 2/5/8/0의 열(y)을 2로 고정하고 행(x)만 변수로 각각 1/2/3/4를 할당해 주었습니다. 더 자세히 설명해보겠습니다.
숫자 2의 좌표값은 (1, 2)입니다.
숫자 5의 좌표값은 (2, 2)입니다.
숫자 8의 좌표값은 (3, 2)입니다.
숫자 0의 좌표값은 (4, 2)입니다.
따라서 숫자 2, 5, 8, 0의 좌표를 (x, 2)와 같이 표현하고 x에만 각각 1/2/3/4를 할당해 주어 반복을 줄이겠다는 의미입니다.
distance_r = abs(current_right_thumb[0] - x) + abs(current_right_thumb[1] - 2)
distance_l = abs(current_left_thumb[0] - x) + abs(current_left_thumb[1] - 2)
# 가독성을 위해 인덴트를 없앴습니다
이제 거리 구하기로 넘어갑니다. 우리는 current_left_thumb와 current_right_thumb 라는 변수에 각각 왼손과 오른손 엄지의 직전 좌표값을 저장해 두고 있어요. 우리가 가고자 하는 2/5/8/0의 좌표값과 왼손, 오른손 사이의 거리를 각각 구해보도록 하겠습니다.
일반화된 식을 쓰기 전에 예시를 가지고 생각을 열어 볼게요. 키패드 2와 6 사이의 거리는 2입니다. 좌표로 어떻게 구할 수 있을까요? 키패드 2의 좌표는 (1, 2), 키패드 6의 좌표는 (2, 3)입니다. x값의 차이 1과 y값의 차이 1을 더해 주면 간단히 2가 나오는 것을 알 수 있어요. 키패드 8과 키패드 1의 경우는 어떨까요? 우선 거리는 3입니다. 좌표로 확인해 봅니다. 키패드 8의 좌표는 (3, 2)입니다. 키패드 1의 좌표는 (1, 1)입니다. x값의 차이 2와 y값의 차이는 1이며 둘을 더해 주면 3이 됩니다. 이제 일반화가 되었습니다.
abs(current_right_thumb[0] - x) : 오른손 엄지손가락의 행좌표와 2/5/8/0의 행좌표(x)의 차(절댓값)
abs(current_right_thumb[1] - 2) : 오른손 엄지손가락의 행좌표와 2/5/8/0의 열좌표(2)의 차(절댓값)
이 두개를 더해 주면 오른손이 움직이는 거리(distance_r)가 되고,
같은 방법으로 왼손이 움직이는 거리(distance_l)도 구해준 거예요.
if distance_r < distance_l:
# 오른손이 가야 하는 거리가 더 짧을 때
answer += "R"
current_right_thumb = (x, 2)
elif distance_r > distance_l:
# 왼손이 가야 하는 거리가 더 짧을 때
answer += "L"
current_left_thumb = (x, 2)
else:
# 왼손 = 오른손 거리가 같을 때
if hand == 'right':
# 오른손잡이라면
answer += "R"
current_right_thumb = (x, 2)
else:
# 왼손잡이라면
answer += "L"
current_left_thumb = (x, 2)
이제 거리를 비교합니다. 오른손이 가야 하는 거리가 더 짧을 때, 왼손이 가야 하는 거리가 짧을 때, 왼손과 오른손이 가야할 거리가 같을 때로 크게 셋으로 나누어 생각합니다. 특히 왼손, 오른손 거리가 같다면 왼손잡이인지 오른손잡이인지를 판단하는 것이 중요합니다.
이제 제가 작성한 함수를 한 번에 보겠습니다.
def solution(numbers, hand):
answer = ""
current_left_thumb = (4, 1)
current_right_thumb = (4, 3)
for number in numbers:
if number == 1:
answer += "L"
current_left_thumb = (1, 1)
elif number == 4:
answer += "L"
current_left_thumb = (2, 1)
elif number == 7:
answer += "L"
current_left_thumb = (3, 1)
elif number == 3:
answer += "R"
current_right_thumb = (1, 3)
elif number == 6:
answer += "R"
current_right_thumb = (2, 3)
elif number == 9:
answer += "R"
current_right_thumb = (3, 3)
elif number in [2, 5, 8, 0]:
if number == 2: x = 1
if number == 5: x = 2
if number == 8: x = 3
if number == 0: x = 4
distance_r = abs(current_right_thumb[0] - x) + abs(current_right_thumb[1] - 2)
distance_l = abs(current_left_thumb[0] - x) + abs(current_left_thumb[1] - 2)
if distance_r < distance_l:
answer += "R"
current_right_thumb = (x, 2)
elif distance_r > distance_l:
answer += "L"
current_left_thumb = (x, 2)
else:
if hand == 'right':
answer += "R"
current_right_thumb = (x, 2)
else:
answer += "L"
current_left_thumb = (x, 2)
return answer
숫자가 2, 5, 8, 0일 때 열 좌표가 2로 모두 동일하다는 점에 착안하여 행 좌표만 x로 변수 할당을 해줌으로써 반복을 줄일 수 있었습니다. 비록 엄청 짧은 코드는 아니지만 깔끔하고 직관적으로 풀이할 수 있어서 좋았습니다!
부족한 풀이었지만 도움이 되었으면 좋겠네요~ 감사합니다.
'Code > problem solving' 카테고리의 다른 글
CPU, 병렬 처리, Ray, Multiprocessing (2) | 2024.09.20 |
---|---|
프로그래머스 코딩테스트 | 2단계 올바른 괄호 (Stack 알고리즘으로 풀이하기) (1) | 2024.04.23 |
프로그래머스 코딩테스트 | 2단계 피보나치 수 문제풀이 (재귀함수 + 메모화로 효율성 올리기) (0) | 2024.04.12 |
오류 해결 | 주피터, 코랩 future warning 제거하기 (pandas, seaborn) (0) | 2024.04.09 |
프로그래머스 코딩테스트 | 1단계 카드뭉치 문제 풀이 (파이썬 sorted와 key) (0) | 2024.04.03 |