미숙한 블로그 주인이 코딩테스트 문제를 풀어가는 과정을 담은 글입니다. 이 풀이가 효율적인 풀이가 아닐 수 있으며, 부정확한 정보가 많이 있을 수 있습니다. 보완해야할 점이 있다면 댓글로 남겨주세요!
https://programmers.co.kr/learn/courses/30/lessons/42860#
조이스틱
문제
조이스틱으로 알파벳 이름을 완성하세요. 맨 처음엔 A로만 이루어져 있습니다.
ex) 완성해야 하는 이름이 세 글자면 AAA, 네 글자면 AAAA조이스틱을 각 방향으로 움직이면 아래와 같습니다.
▲ - 다음 알파벳 ▼ - 이전 알파벳 (A에서 아래쪽으로 이동하면 Z로) ◀ - 커서를 왼쪽으로 이동 (첫 번째 위치에서 왼쪽으로 이동하면 마지막 문자에 커서) ▶ - 커서를 오른쪽으로 이동 (마지막 위치에서 오른쪽으로 이동하면 첫 번째 문자에 커서)
예를 들어 아래의 방법으로 "JAZ"를 만들 수 있습니다.
- 첫 번째 위치에서 조이스틱을 위로 9번 조작하여 J를 완성합니다. - 조이스틱을 왼쪽으로 1번 조작하여 커서를 마지막 문자 위치로 이동시킵니다. - 마지막 위치에서 조이스틱을 아래로 1번 조작하여 Z를 완성합니다. 따라서 11번 이동시켜 "JAZ"를 만들 수 있고, 이때가 최소 이동입니다.
만들고자 하는 이름 name이 매개변수로 주어질 때, 이름에 대해 조이스틱 조작 횟수의 최솟값을 return 하도록 solution 함수를 만드세요.
제한사항
- name은 알파벳 대문자로만 이루어져 있습니다.
- name의 길이는 1 이상 20 이하입니다.
풀이
입출력 예시
풀이
이 문제가 그리디 알고리즘 문제라고 되어있다. 그러나, 그리디로 풀면 정답이 아니다. 아래는 항상 최소 이동 거리만 쫓아가는 방식의 풀이이다.
// 그리디를 이용한 풀이. 정답은 아니다.
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
using namespace std;
const int visited = 9999;
int solution(string name) {
int answer = 0;
map<char, int> alp;
int name_len = name.length();
vector<int> dist(name_len, visited);
vector<bool> setting(name_len, false);
int mid = name_len / 2;
alp['A'] = 0;
for (int i = 1; i < 13; i++) {
alp['A' + i] = i;
alp['A' + 26 - i] = i;
}
alp['N'] = 13;
for (int i = 0; i < name_len; i++) {
if (name[i] != 'A') {
if (i > mid) {
dist[i] = name_len - i;
}
else {
dist[i] = i;
}
}
else {
dist[i] = visited;
setting[i] = true;
}
}
answer += alp[name[0]];
setting[0] = true;
dist[0] = visited;
// 가장 가까운 곳을 계속 찾아간다.
while (find(setting.begin(), setting.end(), false) != setting.end()) {
int min_idx = min_element(dist.begin(), dist.end()) - dist.begin();
answer += alp[name[min_idx]];
answer += dist[min_idx];
dist[min_idx] = visited;
setting[min_idx] = true;
// 거리 갱신
for (int i = 0; i < name_len; i++) {
if (dist[i] != visited) {
if (abs(min_idx - i) > mid) {
if (i > mid) {
dist[i] = name_len + min_idx - i;
}
else {
dist[i] = name_len - min_idx - i;
}
}
else {
dist[i] = abs(min_idx - i);
}
}
}
}
return answer;
}
그렇다면 어떻게 해야할까?
별 수 없다. 왼쪽으로, 오른쪽으로 이동하는 경우를 모두 다 비교해 최솟값을 찾아야한다.
#include <string>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
int joystick(string name, map<char, int> alp, vector<bool> setting, int pos, int dist) {
int result = 0;
int name_len = name.length();
int mid = name_len / 2;
int left = pos;
int right = pos;
int lmove = 0;
int rmove = 0;
result += alp[name[pos]];
result += dist;
setting[pos] = true;
if (find(setting.begin(), setting.end(), false) == setting.end()) {
return result;
}
else {
// 1. 왼쪽으로 이동
while (setting[left] == true) {
left--;
lmove++;
if (left < 0) {
left += name_len;
}
}
// 2. 오른쪽으로 이동
while (setting[right] == true) {
right++;
rmove++;
if (right > name_len - 1) {
right -= name_len;
}
}
// 3. 둘 중 더 작은 값을 반환
return result + min(joystick(name, alp, setting, left, lmove), joystick(name, alp, setting, right, rmove));
}
}
int solution(string name) {
int answer = 0;
map<char, int> alp;
int name_len = name.length();
vector<bool> setting(name_len, false);
int mid = name_len / 2;
alp['A'] = 0;
for (int i = 1; i < 13; i++) {
alp['A' + i] = i;
alp['A' + 26 - i] = i;
}
alp['N'] = 13;
for (int i = 0; i < name_len; i++) {
if (name[i] == 'A') {
setting[i] = true;
}
}
answer = joystick(name, alp, setting, 0, 0);
return answer;
}
'코딩 테스트(Coding test) > Lv. 2' 카테고리의 다른 글
[프로그래머스/C++] [3차] 방금그곡 (0) | 2022.05.11 |
---|---|
[프로그래머스/C++] 후보키 (0) | 2022.05.10 |
[프로그래머스/C++] 거리두기 확인하기 (1) | 2022.05.04 |
[프로그래머스/C++] 쿼드압축 후 개수 세기 (0) | 2022.04.28 |
[프로그래머스/C++] 수식 최대화 (0) | 2022.04.26 |