[프로그래머스] 조이스틱 (자바스크립트, JavaScript)

young_pallete·2021년 8월 1일

Algorithm

목록 보기
6/32

시작하며 🔥

으, 화나네요!
풀자마자 드는 생각은 이게 과연 그리디일까 싶었어요.
왜냐하면, 현재의 최적의 답안이, 곧 해로 접근할 수 있는 과정이 아니었기 때문이죠.
질문란을 보니, 꽤나 많은 사람들이 공감을 했었네요. 네, 이건 엄연히 말하자면 그리디가 아닙니다.

결국, 허탈함에 우선순위 큐로 풀었어요...ㅋㅋㅋㅋㅋㅋㅋㅋㅋ
그나마 한 방에 풀리지 않았다면, 멘탈이 바사삭이었을 듯합니다.

그래도, 이 문제는 꽤나 접근하기 까다로웠어요. 저한테는 말이죠.
그 과정을, 지금부터 살펴보겠습니다.

풀이 과정 📃

이게 과연 그리디인가 싶었을 때, 아차 싶어서 좀 더 유연한 사고를 하자고 생각하며 다른 방식으로 풀었습니다.
그때의 제 뇌리 속의 과정은 다음과 같았어요.

  1. 결국에 이 문제의 핵심은 인덱스를 어느 방향으로 접근하는 것이 가장 최소 횟수로 접근할 수 있느냐를 묻는 것이다.
  2. 따라서, 인덱스를 접근하는 횟수와 알파벳을 바꾸는 횟수를 따로 분리해서 접근한다. ⭐
  3. 먼저 알파벳을 바꿀 때마다 인덱스를 어떤 배열에 담는다.
  4. 이때, 우선순위 큐에 넣기 전에 어떤 indexArr(바꿀 인덱스 값들을 담아놓은 배열)의 크기를 살펴보자.
    4-1. if (indexArr.length === 0) return 0이다. 바꾼 게 없기 때문이다.
    4-2. if (indexArr.length >= 1) 인덱스 양에 따라서 가장 앞쪽 인덱스와 가장 뒤쪽 인덱스에 맞춰 (현재 이동 횟수, 현재 위치, 앞으로 남은 indexArr)을 우선순위 큐에 넣는다.
  5. 우선순위 큐에서 앞으로 남은 indexArr가 빈 배열이 리턴될 때까지 4-2번을 반복한다. 만약 이를 만족하면 최소 횟수를 가져온다.
  6. 알파벳 치환 횟수 + 리턴된 인덱스 접근 최소 횟수를 리턴한다.
class MinHeap {
    constructor() {
        this.heap = [null];
    }
    updateParentIndex(index) {
        return Math.floor(index / 2);
    }
    updateIndices(index) {
        return [ index, index * 2, index * 2 + 1]
    }
    swap(a, b) {
        [this.heap[a], this.heap[b]] = [this.heap[b], this.heap[a]];
    }
    heappush(val) {
        this.heap.push(val);
        let nowIndex = this.heap.length - 1;
        let parentIndex = this.updateParentIndex(nowIndex);
        while(nowIndex > 1 && this.heap[parentIndex][0] > this.heap[nowIndex][0]) {
            this.swap(nowIndex, parentIndex)
            nowIndex = parentIndex;
            parentIndex = this.updateParentIndex(nowIndex);
        }
    }
    heappop() {
        if (this.heap.length === 1) return;
        if (this.heap.length === 2) return this.heap.pop();
        const min = this.heap[1];
        this.heap[1] = this.heap.pop();
        let [ nowIndex, leftIndex, rightIndex ] = this.updateIndices(1);
        if (!this.heap[leftIndex]) return min;
        if (!this.heap[rightIndex]) { 
            if (this.heap[leftIndex][0] < this.heap[nowIndex][0]) {
                this.swap(leftIndex, nowIndex);
            }
            return min;
        }
        while ((this.heap[leftIndex][0] < this.heap[nowIndex][0]) || (this.heap[rightIndex][0] < this.heap[nowIndex][0])) {    
            const minIndex = this.heap[leftIndex][0] <= this.heap[rightIndex][0] ? leftIndex : rightIndex;
            this.swap(nowIndex, minIndex);
            [ nowIndex, leftIndex, rightIndex ] = this.updateIndices(minIndex);
            if (!this.heap[leftIndex]) return min;
            if (!this.heap[rightIndex]) { 
                if (this.heap[leftIndex][0] < this.heap[nowIndex][0]) {
                    this.swap(leftIndex, nowIndex);
                }
                return min;
            }
        }
        return min;
    }
    print() {
        console.log(this.heap)
    }
    getLength() {
        return this.heap.length;
    }
}

const getMinChangeCount = (before, after) => {
    const count = Math.abs(before.charCodeAt(0) - after.charCodeAt(0));
    return Math.min(count, 26 - count);
};
const createPushedArr = (name, beforeCount, nowIndex, nextIndex, arr) => {
    const count = Math.max(nowIndex, nextIndex) - Math.min(nowIndex, nextIndex);
    return [beforeCount + Math.min(count, name.length - count), nextIndex, arr.filter(value => value !== nextIndex)]
}
const getResult = (name, minHeap) => {
    while(true) {
        const [cnt, idx, arr] = minHeap.heappop();
        if (arr.length === 0) return cnt;
        if (arr.length >= 1) {
            if (arr.length > 1) minHeap.heappush(createPushedArr(name, cnt, idx, arr[arr.length - 1], arr))
            minHeap.heappush(createPushedArr(name, cnt, idx, arr[0], arr))
        }
    }
}

const solution = name => {
    let answer = 0;
    const indexArr = [];
    const minHeap = new MinHeap();
    for (let i = 0; i < name.length; i++) {
        const now = name[i];
        if (now !== 'A') {
            if (now !== 0) indexArr.push(i); // 현재 가야 할 인덱스 번호 넣기
            answer += getMinChangeCount('A', now);
        }
    }
    // [ 목표 인덱스로 갔을 때 횟수, 간 결과 인덱스 위치, 남은 인덱스]
    if (indexArr.length === 0) return 0;
    if (indexArr.length >= 1) {
        if (indexArr.length > 1) minHeap.heappush(createPushedArr(name, 0, 0, indexArr[indexArr.length - 1], indexArr))
        minHeap.heappush(createPushedArr(name, 0, 0, indexArr[0], indexArr))
    }
    answer += getResult(name, minHeap);
    return answer;
}

마치며

15점을 주는군요. 이게 난이도에 따라서 주는 건지 잘 모르겠지만, 적어도 제게 꽤나 난이도가 적지 않았네요. (그리디라는 말만 아니었으면 오히려 더 쉬웠을지도...?)
일단 프로그래머스는 테스트케이스가 꽤나 약한 것 같아요. 이부분은 차차 고쳐졌으면 합니다..!

여튼, 아닌 새벽에 머리가 땡겼던 문제. 심지어 힙 구현도 JS라 어지간히 힘드네요. 😂 그래도 이제는 안 보고도 쓸 정도로 이해를 확실히 할 수 있게 됐어요!
가까스로 홀로 클리어해낸다는 게 참 기쁘고, 정말 다행이에요 😋👍

++++

저는 혹여나 만약 오른쪽 조이스틱 이동이 마지막 위치에서 첫번째 위치로 갈 수 있다는 가정하에 작성했어요. 만약 이것이 불가하다면, createPushedArr 부분을 바꿔주면 되겠죠?!

혹여나 나중에 업데이트 돼서 이 글이 💩글이 될 수도 있으니, 이 부분 유의해주세요~

profile
People are scared of falling to the bottom but born from there. What they've lost is nth. 😉

0개의 댓글