코딩테스트

[프로그래머스] 퍼즐게임첼린지 자바 이진탐색

mhui123 2025. 4. 12. 16:04
반응형

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

 

프로그래머스

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

programmers.co.kr

 

문제

/**
 *
 * @param diffs 난이도 모음
 * @param times time_cur 모음
 * @param limit 총 제한 시간.
 * @return limit안에 모든 퍼즐을 풀 수 있는 최소 level
 */

접근

diffs에서 최고 레벨을 구한 후 / 2씩 범위를 줄여가며 찾으면 좋겠다고 생각했다.
그러나 어떤 방식으로 diff / 2 한 후 +-로 조정해야 할지 갈피를 잡지 못했음.
public int solution(int[] diffs, int[] times, long limit) {
        int minLevel = 1;
        int maxLevel = 0;

        //모든 스테이지를 다 클리어 했을 때 총 소요시간이 limit와 근사한 값이 나오면 최소 레벨.
        //최고 스테이지 먼저 찾아봄
        for(int i = 0; i < diffs.length; i ++){
            maxLevel = Math.max(maxLevel, diffs[i]);
        }


        int test = maxLevel / 2;
        getMidClearLevel(diffs, times, limit, test);

        return minLevel;
    }

    public int getMidClearLevel(int[] diffs, int[] times, long limit, int level){
        long time_prev = 0;
        for(int i = 0; i < diffs.length; i ++){
            int diff = diffs[i];
            int time_cur = times[i];
            if(diff > level){
                time_prev += time_cur;
            } else {
                time_prev += (diff - level) * (time_prev + time_cur);
            }
        }

        if(time_prev > limit) getMidClearLevel(diffs, times, limit, level + 1);

        return level;
    }

2차시도

GPT의 도움으로 코드를 수정했다. 반복 처리를 하며 범위를 좁혀가는 방식이었다.
하지만 제출 후 14번 케이스의 계속적인 실패 발생
 public int solution2(int[] diffs, int[] times, long limit) {
        int left = 0; // 최소 난이도
        int right = 100000; // 최대 레벨 범위 (제한이 없으면 적당히 크게 잡기)

        int answer = -1;

        while (left <= right) {
            int mid = (left + right) / 2;

            if (canClearAll(diffs, times, mid, limit)) {
                answer = mid;
                right = mid - 1; // 더 낮은 레벨로 시도
            } else {
                left = mid + 1; // 더 높은 레벨로 올려야 함
            }
        }
        answer = answer == 0 ? 1 : answer;
        return answer;
    }

    private boolean canClearAll(int[] diffs, int[] times, int level, long limit) {
        long totalTime = 0;
        for (int i = 0; i < diffs.length; i++) {
            if (diffs[i] <= level) {
                totalTime += times[i];
            } else {
                int time_prev = i == 0 ? 0 : times[i - 1];
                long penalty =  Math.max(0L, (long) diffs[i] - level);
                totalTime += (penalty * (time_prev + times[i]) + times[i]);

            }

            // 시간 초과 방지
            if (totalTime > limit) {
                return false;
            }
        }

        return true;
    }

다른 사람의 답변 참고

https://jincchan.tistory.com/57
동일하게 14번케이스에서 문제가 발생한 사람들이 있었다.
그 중 제일 맘에든 케이스를 참고하였다.
public int solution3(int[] diffs, int[] times, long limit) {
        int maxValue = Arrays.stream(diffs).max().isPresent()? Arrays.stream(diffs).max().getAsInt() : 0;
        int minValue = 1;

        while (minValue < maxValue) {
            int level = (maxValue + minValue) / 2;
            long time = calTime(diffs, times, level);

            if(limit >= time) {
                maxValue = level;
            } else {
                minValue = level+1;
            }
        }
        return maxValue;
    }
    private long calTime(int[] diffs, int[] times, int level){
        long time = 0;
        for(int i = 0; i < diffs.length; i++){
            if(diffs[i] <= level){
                time += times[i];
            } else {
                int prev_time = i == 0 ? 0 : times[i -1];
                time += (long) (prev_time + times[i]) * (diffs[i] - level) + times[i];
            }
        }
        return time;
    }

차이 요약

  solution2 solution3
이진 탐색 범위 [0, 100000] 등 제한된 범위로 고정 min = 1, max = max(diffs)로 유동적
종료 조건 left <= right + answer == 0이면 1로 강제 설정 min < max, 반환값 maxValue 자체로 결정
패널티 적용 Math.max(0, diff - level)로 예외 처리 무조건 (diff - level) 사용 (단, level은 절대 작지 않음)
반환 결과 보정 answer == 0 → 1 처리 없음
반응형