[2023 KAKAO BLIND RECRUITMENT] 택배 배달과 수거하기

최민길(Gale)·2023년 2월 22일
1

알고리즘

목록 보기
42/172

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

[이 문제는 프로그래머스에서 푼 문제입니다.]
이 문제는 그리디 알고리즘과 스택을 이용하여 풀 수 있습니다. 그리디 알고리즘이란 각 단계에서 가장 최선의 선택을 하는 기법으로, 이 문제의 경우 정답을 도출하기 위해선 크게 3가지만 고려하면 됩니다.

  1. 가장 먼 집부터 작업 진행
  2. 갈 때는 최대 적재량으로 출발
  3. 올 때는 최대 적재량으로 짐 싣고 오기

즉 쉽게 말해, 무조건 먼 쪽의 집에서부터 작업을 진행하면 되고, 실행 시 최대 적재량을 가득 채운 상태로 작업을 진행하면 최단 거리를 구할 수 있습니다. 이를 구현하기 위해 만약 완전탐색으로 진행하게 된다면 배달과 수거를 진행할 집을 탐색하고 그 내부에서 현재 집에서 싣을 수 있는 택배 수를 넘었는지 여부를 따져가며 다시 반복문을 돌아 탐색하게 되는데 이 경우 시간 초과가 발생하게 됩니다. 이를 해결하기 위해 가장 먼 집을 가장 먼저 진행하기 때문에 스택을 사용하여 집 주소를 탐색하면 배달과 수거를 진행할 집을 탐색하는 소요가 없어지고 스택의 peek값으로 쉽게 찾을 수 있기 때문에 시간 복잡도가 감소합니다.

다음은 코드입니다.

import java.util.*;

class Solution {
    public long solution(int cap, int n, int[] deliveries, int[] pickups) {
        long answer = 0;
        
        // 그리디
        // 1. 가장 먼 집부터 작업 진행
        // 2. 갈 때는 최대 적재량으로 출발
        // 3. 올 때는 최대 적재량으로 짐 싣고 오기
        
        // 택배 배달, 박스 수거 각각 스택 생성
        Stack<Integer> deliveriesStack = new Stack<>();
        Stack<Integer> pickupsStack = new Stack<>();
        
        // 택배 배달, 박수 수거 각각 0이 아닌 값들을 앞에서 순서대로 스택에 추가
        // 그렇게 되면 스택에서 값을 뽑을 때 가장 마지막 집이 선택
        for(int i=0;i<n;i++){
            if(deliveries[i]!=0) deliveriesStack.push(i);
            if(pickups[i]!=0) pickupsStack.push(i);
        }
        
        // 왕복해서 갈 때는 배달, 올 때는 수거를 진행하므로
        // 배달 스택과 수거 스택의 peek()를 비교하여 더 큰 수의 *2 만큼 answer 더하기
        while(!deliveriesStack.isEmpty() || !pickupsStack.isEmpty()){
            // 만약 두 스택 중 하나가 다 비었다면 무조건 남은 친구를 뽑고 그 수의 *2 만큼 answer 더하기
            
            // 1. 배달 스택이 비었을 경우 : 수거 스택만 뽑은 후 그 수의 *2 만큼 더하기
            if(deliveriesStack.isEmpty() && !pickupsStack.isEmpty()){
                int adder = getAdder(pickupsStack,cap,pickups);
                answer += (adder+1)*2;
                
                // 택배 
            }
            // 2. 수거 스택이 비었을 경우 : 배달 스택만 뽑은 후 그 수의 *2 만큼 더하기
            else if(!deliveriesStack.isEmpty() && pickupsStack.isEmpty()){
                int adder = getAdder(deliveriesStack,cap,deliveries);
                answer += (adder+1)*2;
            }
            // 3. 둘 다 비어있지 않을 경우 : 더 큰 수의 *2 만큼 더하기
            else{
                int deliveriesPeek = getAdder(deliveriesStack,cap,deliveries);
                int pickupsPeek = getAdder(pickupsStack,cap,pickups);
                int adder = Math.max(deliveriesPeek, pickupsPeek);
                answer += (adder+1)*2;
            }
            
        }
        
        return answer;
    }
    
    static int getAdder(Stack<Integer> stack, int cap, int[] arr){
        int currCap = 0;
        int adder = 0;
        while(cap > currCap){
            // 만약 스택이 비었다면 종료
            if(stack.isEmpty()) break;
            
            int peek = stack.peek();
            adder = Math.max(adder,peek);

            // 만약 현재 집에서 싣을 수 있는 택배 수를 넘었을 경우
            if(arr[peek] + currCap > cap){
                // 차이만큼 더하고 pop 하지 않고 deliveries[] 값을 차이만큼 뺀다
                int diff = cap - currCap;
                arr[peek] -= diff;
                currCap = cap;
            }

            // 만약 현재 집에서 싣을 수 있는 택배 수를 넘지 않았을 경우
            else{
                // currCap에 해당값 그대로 더하고 pop
                currCap += arr[peek];
                stack.pop();
            }

        }
        return adder;
    }
}

profile
저는 상황에 맞는 최적의 솔루션을 깊고 정확한 개념의 이해를 통한 다양한 방식으로 해결해오면서 지난 3년 동안 신규 서비스를 20만 회원 서비스로 성장시킨 Software Developer 최민길입니다.

0개의 댓글

관련 채용 정보