[프로그래머스] 퍼즐게임 챌린지 - JavaScript

2025. 2. 25. 16:41·알고리즘

문제 출처

https://school.programmers.co.kr/learn/courses/30/lessons/340212

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

문제 설명

당신은 순서대로 n개의 퍼즐을 제한 시간 내에 풀어야 하는 퍼즐 게임을 하고 있습니다. 각 퍼즐은 난이도와 소요 시간이 정해져 있습니다. 당신의 숙련도에 따라 퍼즐을 풀 때 틀리는 횟수가 바뀌게 됩니다. 현재 퍼즐의 난이도를 diff, 현재 퍼즐의 소요 시간을 time_cur, 이전 퍼즐의 소요 시간을 time_prev, 당신의 숙련도를 level이라 하면, 게임은 다음과 같이 진행됩니다.

  • diff ≤ level이면 퍼즐을 틀리지 않고 time_cur만큼의 시간을 사용하여 해결합니다.
  • diff > level이면, 퍼즐을 총 diff - level번 틀립니다. 퍼즐을 틀릴 때마다, time_cur만큼의 시간을 사용하며, 추가로 time_prev만큼의 시간을 사용해 이전 퍼즐을 다시 풀고 와야 합니다. 이전 퍼즐을 다시 풀 때는 이전 퍼즐의 난이도에 상관없이 틀리지 않습니다. diff - level번 틀린 이후에 다시 퍼즐을 풀면 time_cur만큼의 시간을 사용하여 퍼즐을 해결합니다.

예를 들어 diff = 3, time_cur = 2, time_prev = 4인 경우, level에 따라 퍼즐을 푸는데 걸리는 시간은 다음과 같습니다.

  • level = 1이면, 퍼즐을 3 - 1 = 2번 틀립니다. 한 번 틀릴 때마다 2 + 4 = 6의 시간을 사용하고, 다시 퍼즐을 푸는 데 2의 시간을 사용하므로 총 6 × 2 + 2 = 14의 시간을 사용하게 됩니다.
  • level = 2이면, 퍼즐을 3 - 2 = 1번 틀리므로, 6 + 2 = 8의 시간을 사용하게 됩니다.
  • level ≥ 3이면 퍼즐을 틀리지 않으며, 2의 시간을 사용하게 됩니다.

퍼즐 게임에는 전체 제한 시간 limit가 정해져 있습니다. 제한 시간 내에 퍼즐을 모두 해결하기 위한 숙련도의 최솟값을 구하려고 합니다. 난이도, 소요 시간은 모두 양의 정수며, 숙련도도 양의 정수여야 합니다.

퍼즐의 난이도를 순서대로 담은 1차원 정수 배열 diffs, 퍼즐의 소요 시간을 순서대로 담은 1차원 정수 배열 times, 전체 제한 시간 limit이 매개변수로 주어집니다. 제한 시간 내에 퍼즐을 모두 해결하기 위한 숙련도의 최솟값을 정수로 return 하도록 solution 함수를 완성해 주세요.


제한사항
  • 1 ≤ diffs의 길이 = times의 길이 = n ≤ 300,000
    • diffs[i]는 i번째 퍼즐의 난이도, times[i]는 i번째 퍼즐의 소요 시간입니다.
    • diffs[0] = 1
    • 1 ≤ diffs[i] ≤ 100,000
    • 1 ≤ times[i] ≤ 10,000
  • 1 ≤ limit ≤ 10^15
    • 제한 시간 내에 퍼즐을 모두 해결할 수 있는 경우만 입력으로 주어집니다.

입출력 예 설명

입출력 예 #1

숙련도가 3인 경우 다음과 같이 진행됩니다.

  • 1번째 퍼즐을 2의 시간을 사용하여 해결합니다.
  • 2번째 퍼즐을 5 - 3 = 2번 틀려서 총 (4 + 2) × 2 + 4 = 16의 시간을 사용하여 해결합니다.
  • 3번째 퍼즐을 7의 시간을 사용하여 해결합니다.

총 2 + 16 + 7 = 25의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 3보다 작은 경우 제한 시간인 30 이내에 모든 퍼즐을 해결할 수 없습니다.

따라서 3을 return 해야 합니다.

입출력 예 #2

숙련도가 2인 경우 다음과 같이 진행됩니다.

  • 1번째 퍼즐을 6의 시간을 사용하여 해결합니다.
  • 2번째 퍼즐을 4 - 2 = 2번 틀려서 총 (3 + 6) × 2 + 3 = 21의 시간을 사용하여 해결합니다.
  • 3번째 퍼즐을 4 - 2 = 2번 틀려서 총 (8 + 3) × 2 + 8 = 30의 시간을 사용하여 해결합니다.
  • 4번째 퍼즐을 2의 시간을 사용하여 해결합니다.

총 6 + 21 + 30 + 2 = 59의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 2보다 작은 경우 제한 시간인 59 이내에 모든 퍼즐을 해결할 수 없습니다.

따라서 2를 return 해야 합니다.

입출력 예 #3

숙련도가 294인 경우 다음과 같이 진행됩니다.

  • 1번째 퍼즐을 2의 시간을 사용하여 해결합니다.
  • 2번째 퍼즐을 328 - 294 = 34번 틀려서 총 (7 + 2) × 34 + 7 = 313의 시간을 사용하여 해결합니다.
  • 3번째 퍼즐을 467 - 294 = 173번 틀려서 총 (1 + 7) × 173 + 1 = 1385의 시간을 사용하여 해결합니다.
  • 4번째 퍼즐을 4의 시간을 사용하여 해결합니다.
  • 5번째 퍼즐을 3의 시간을 사용하여 해결합니다.

총 2 + 313 + 1385 + 4 + 3 = 1707의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 294보다 작은 경우 제한 시간인 1723 이내에 모든 퍼즐을 해결할 수 없습니다.

따라서 294를 return 해야 합니다.

입출력 예 #4

숙련도가 39354인 경우 다음과 같이 진행됩니다.

  • 1번째 퍼즐을 9999의 시간을 사용하여 해결합니다.
  • 2번째 퍼즐을 99999 - 39354 = 60645번 틀려서 총 (9001 + 9999) × 60645 + 9001 = 1152264001의 시간을 사용하여 해결합니다.
  • 3번째 퍼즐을 100000 - 39354 = 60646번 틀려서 총 (9999 + 9001) × 60646 + 9999 = 1152283999의 시간을 사용하여 해결합니다.
  • 4번째 퍼즐을 99995 - 39354 = 60641번 틀려서 총 (9001 + 9999) × 60641 + 9001 = 1152188001의 시간을 사용하여 해결합니다.

총 9999 + 1152264001 + 1152283999 + 1152188001 = 3456746000의 시간을 사용하여 모든 퍼즐을 해결할 수 있습니다. 숙련도가 39354보다 작은 경우 제한 시간인 3456789012 이내에 모든 퍼즐을 해결할 수 없습니다.

따라서 39354를 return 해야 합니다.

풀이방법

  • 이진 탐색을 통해 풀었다. 범위가 30만 까지이고, 이 안에서 완전 탐색을 수행하면 무조건 시간 초과가 날게 뻔하다.
  • left를 1로, right를 max로 선언하여 이진 탐색을 수행하면 된다.

풀이하면서 생긴 문제점...

let right = Math.max(...diffs);
  • 처음엔 위와 같이 max를 찾아 최댓값을 찾으려했다. 어짜피 level이 저 원소들보다 높은경우 무조건 1회 연산만 수행하기에 limit을 넘을 수 없다고 판단했다.
  • 그러나 테스트케이스 15번부터 런타임에러가 났다...
    • 검색을 해보니, 저런 식으로 코드를 작성하면 최대 30만 크기의 배열에서 스프레드 연산자로 뿌릴경우, 1~2만개의 자바스크립트 호출 스택이 발생하여 Maximum call Stack 오류가 난다고 한다...
    • 출처 - https://school.programmers.co.kr/questions/80164
 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

let right = 100000;
let [left, right] = [1, diffs.reduce((acc, cur) => Math.max(acc, cur), 1)];
  • 위 코드로 max 값을 찾으면되는데 배열의 크기가 작을경우는 아래가, 클 경우는 위가 유리하므로 편한대로 쓰면 될 것 같다.

풀이 코드

function solution(diffs, times, limit) {
    let answer = 0;
    let [left, right] = [1, diffs.reduce((acc, cur) => Math.max(acc, cur), 1)]
    
    while(left <= right){
        //mid를 레벨로
        let level = Math.floor((left + right) / 2);
        let totalTime = 0;
        
        for(let i = 0; i < diffs.length; i++){
            if(diffs[i] <= level){
                totalTime += times[i];
            }
            else {
                totalTime += ((diffs[i] - level) * (times[i] + (i > 0 ? times[i - 1] : 0))) + times[i];
            }
            //범위넘어가면 가지치기
            if(totalTime > limit){
                break;
            }
        }
        
        if(totalTime <= limit){
            answer = level;
            right = level - 1;
        }
        else {
            left = level + 1;
        }
    }
    
    return answer;
}

결과

회고

  • 문제 자체는 쉬운 것 같은데, 스택 오버플로우는 오늘 처음 알아간다.
저작자표시 비영리 동일조건 (새창열림)

'알고리즘' 카테고리의 다른 글

[백준] 선수과목 - Java  (1) 2025.02.28
[프로그래머스] 광물 캐기 - JavaScript  (3) 2025.02.26
[프로그래머스] 지게차와 크레인 - JavaScript  (1) 2025.02.24
[프로그래머스] 완전범죄 - JavaScript  (1) 2025.02.20
[프로그래머스] 서버 증설 횟수 - JavaScript  (1) 2025.02.19
'알고리즘' 카테고리의 다른 글
  • [백준] 선수과목 - Java
  • [프로그래머스] 광물 캐기 - JavaScript
  • [프로그래머스] 지게차와 크레인 - JavaScript
  • [프로그래머스] 완전범죄 - JavaScript
BGK97
BGK97
사용자 입장에서 한번 더 생각하는 개발자로 성장하고 싶은 사람입니다.
  • BGK97
    꾸준히, 열심히
    BGK97
  • 전체
    오늘
    어제
    • 분류 전체보기 (111)
      • 알고리즘 (73)
      • 컴퓨터 구조와 운영 체제(책) (8)
      • 네트워크 (5)
      • React (10)
      • 경험한 에러들 (3)
      • HTML, CSS, JavaScript (8)
      • 자료구조 (1)
      • 이것이 컴퓨터 과학이다(책) (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
BGK97
[프로그래머스] 퍼즐게임 챌린지 - JavaScript
상단으로

티스토리툴바