-
[Algorithm: 그리디] 프로그래머스 <조이스틱>Algorithms 2026. 4. 28. 20:02Greedy Java Programmers Lv.2
문제
https://school.programmers.co.kr/learn/courses/30/lessons/42860?language=java
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
문제 설명
조이스틱으로 알파벳을 만든다. 커서는 맨 앞에서 시작하고, 좌우로 이동하면서 각 위치의 알파벳을 조이스틱으로 조작해 원하는 이름을 완성한다. 최소 조작 횟수를 구하는 문제다.
▲ : 알파벳 +1 (A → B → ... → Z → A)
▼ : 알파벳 -1 (A → Z → Y → ...)
◀ : 커서를 왼쪽으로 (맨 왼쪽에서 누르면 맨 오른쪽으로)
▶ : 커서를 오른쪽으로 (맨 오른쪽에서 누르면 맨 왼쪽으로)문제 분해: 두 문제가 독립적이다
처음엔 상하 이동과 좌우 이동이 얽혀있는 것처럼 보이지만, 사실 이 둘은 완전히 독립적으로 계산할 수 있다.
① 상하 조작 비용 — 각 알파벳을 만드는 최솟값의 합
② 좌우 이동 비용 — 모든 위치를 방문하는 최솟값
최종 답 = ① + ②① 상하 조작 비용
각 알파벳은 위로 올리거나 아래로 내리는 두 가지 방법이 있다. 알파벳은 원형이므로 둘 중 더 짧은 방향을 선택하면 된다.
알파벳 원형 다이얼 (A=0, Z=25)
알파벳 A B C ... N ... Y Z예시: 'N' 만들기▲ 위로 A →→→ +13 →→→ N 13번▼ 아래로 A ←←← -13 ←←← N 13번→ N은 정중앙이라 어느 방향이든 13번으로 동일
int up = c - 'A'; // 위로 올리는 횟수 int down = 'Z' - c + 1; // 아래로 내리는 횟수 (원형 연결 고려) answer += Math.min(up, down); // 둘 중 최솟값 선택② 좌우 이동 비용: 어려운 부분
커서는 인덱스 0에서 시작한다. 모든 A가 아닌 위치를 방문해야 한다. 연속된 A 구간은 건너뛸 수 있으므로, 어떻게 이동하면 가장 적게 이동할 수 있을까?
관찰: 알파벳이 'A'인 위치는 조작이 필요 없다.
연속된 A 구간을 만나면, 왼쪽에서 돌아가는 것과 오른쪽으로 계속 가는 것 중 어느 쪽이 더 짧은지 비교해야 한다.3가지 이동 전략
입력
"JAAN"을 예시로 보자. 인덱스 1, 2가 연속 A이고, 끝에 N이 있다.예시: "JAAN" (인덱스 1, 2가 연속 A)
J0A1A2N3i=0 기준, next=3 (인덱스 1, 2는 A이므로 건너뜀 → 처음 나오는 유효 위치)
n = 4, i = 0, next = 3전략 A — 그냥 오른쪽으로 쭉move = name.length() - 1 = 3 // 0→1→2→3, 3번 이동 (A도 그냥 지나침)전략 B — 오른쪽으로 i칸 갔다가 되돌아와 왼쪽(wrap)으로 접근i * 2 + (n - next) // 0에서 오른쪽으로 i칸 → 다시 0으로 되돌아오기 i칸 → 왼쪽 wrap으로 끝에서 (n-next)칸
= 0 * 2 + (4 - 3) = 1 // 오른쪽 이동 없이 바로 wrap → 끝에서 N까지 1칸전략 C — 왼쪽(wrap)으로 먼저 접근하고 되돌아와 오른쪽으로i + (n - next) * 2 // 0에서 왼쪽 wrap으로 끝에서 (n-next)칸 → 다시 0으로 되돌아오기 (n-next)칸 → 오른쪽으로 i칸
= 0 + (4 - 3) * 2 = 2 // wrap 1칸 + 되돌아오기 1칸, 오른쪽 이동 없음min(3, 1, 2) = 1 → 전략 B 선택: 바로 왼쪽 wrap으로 N 방문공식 직관적으로 이해하기
연속된 A 구간을 피하기 위한 두 가지 우회 전략을 이렇게 생각하면 된다.
이동 전략 시각화 — "NBAAN" (인덱스 1, 2가 A, 끝에 N 존재)
N0A1A2B3N4i=0, n=5, next=3 (인덱스 1, 2는 A → 건너뜀, next=3)
전략 A: move = n-1 = 4 (오른쪽으로 쭉)전략 B:
i*2 + (n - next)
0에서 오른쪽으로 i=0칸 → 되돌아오기 0칸 → 왼쪽 wrap으로 끝에서 n-next=2칸 (4→3)
= 0*2 + (5-3) = 2전략 C:
i + (n - next)*2
왼쪽 wrap으로 끝까지 n-next=2칸 → 0으로 되돌아오기 +2칸 → 오른쪽으로 i=0칸
= 0 + (5-3)*2 = 4min(4, 2, 4) = 2 → 전략 B 선택: 바로 wrap으로 오른쪽 끝부터 접근i = 현재 위치 (여기까지는 이미 방문했다고 볼 수 있음)
next = 연속 A를 건너뛴 뒤 처음 나오는 유효 위치
n - next = 오른쪽 끝에서 wrap해서 접근해야 할 거리
전체 코드 흐름
1move를 "오른쪽으로 쭉 가는 경우"인n-1로 초기화2각 위치에서 상하 조작 비용을answer에 누적3각 위치에서 A 연속 구간의 끝next를 찾아, 두 가지 우회 전략과 현재move를 비교해 최솟값 갱신4최종answer + move반환최종 코드
class Solution { public int solution(String name) { int answer = 0; int move = name.length() - 1; // 기본값: 오른쪽으로 쭉 for (int i = 0; i < name.length(); i++) { char c = name.charAt(i); // ① 상하 조작 비용 int up = c - 'A'; int down = 'Z' - c + 1; answer += Math.min(up, down); // ② 좌우 이동 비용: 연속 A 구간의 끝 탐색 int next = i + 1; while (next < name.length() && name.charAt(next) == 'A') { next++; } // 전략 B: 오른쪽으로 i칸 갔다가 되돌아와 왼쪽으로 우회 move = Math.min(move, i * 2 + name.length() - next); // 전략 C: 왼쪽으로 먼저 우회하고 오른쪽으로 move = Math.min(move, i + (name.length() - next) * 2); } return answer + move; } }예시 추적: "JAZ"
"JAZ" — 상하 조작 비용 계산
J0A1Z2J: up=9, down=17 → min=9
A: up=0, down=26 → min=0
Z: up=25, down=1 → min=1
상하 합계 = 10"JAZ" — 좌우 이동 비용 (n=3, 초기 move=2)
i=0: next=2 (인덱스 1은 A, 건너뜀)
전략 B: 0*2 + (3-2) = 1
전략 C: 0 + (3-2)*2 = 2
move = min(2, 1, 2) = 1
i=1: next=2
전략 B: 1*2 + (3-2) = 3
전략 C: 1 + (3-2)*2 = 3
move = min(1, 3, 3) = 1 (변화 없음)move 최솟값 = 1최종 answer = 상하(10) + 이동(1) = 11
정리
문제 분해상하 조작과 좌우 이동은 독립적으로 계산 가능. 각각 최솟값을 구해 더한다.상하 조작알파벳은 원형. 위로 올리기(c-'A')와 내리기('Z'-c+1) 중 min 선택.연속 A 구간A는 조작이 필요 없다. 연속 A 구간을 발견하면 어느 방향으로 우회할지 비교.두 가지 우회전략 B: i*2 + (n-next)
전략 C: i + (n-next)*2
매 위치에서 두 값과 현재 move를 min으로 갱신.복잡해 보이는 문제는 독립된 하위 문제로 분해하는 것이 핵심이다.
좌우 이동에서는 "연속 A 구간을 어느 방향으로 우회하는가"라는 단 하나의 질문으로 좁혀진다.* 이 포스팅은 Claude AI의 도움을 받아서 작성되었습니다.
'Algorithms' 카테고리의 다른 글
[Algorithm: DFS] 프로그래머스 <여행경로> (0) 2026.05.01 [Algorithm: DFS] 프로그래머스 <네트워크> (0) 2026.05.01 [Algorithm: 해시] 프로그래머스 <전화번호 목록 > (0) 2026.04.27 [Algorithm: 스택/큐] 프로그래머스 <올바른 괄호> (0) 2026.04.26 [Algorithm: BFS] 프로그래머스 <게임 맵 최단거리> (0) 2026.03.30