[BOJ] 8980.택배

백준 알고리즘 문제 풀이

해당 문제는 그리디 방식으로 풀어보았습니다.

링크 : image


1. 문제

문제 보기
시간 제한메모리 제한
1 초128 MB

직선 도로상에 왼쪽부터 오른쪽으로 1번부터 차례대로 번호가 붙여진 마을들이 있다. 마을에 있는 물건을 배송하기 위한 트럭 한 대가 있고, 트럭이 있는 본부는 1번 마을 왼쪽에 있다. 이 트럭은 본부에서 출발하여 1번 마을부터 마지막 마을까지 오른쪽으로 가면서 마을에 있는 물건을 배송한다.

각 마을은 배송할 물건들을 박스에 넣어 보내며, 본부에서는 박스를 보내는 마을번호, 박스를 받는 마을번호와 보낼 박스의 개수를 알고 있다. 박스들은 모두 크기가 같다. 트럭에 최대로 실을 수 있는 박스의 개수, 즉 트럭의 용량이 있다. 이 트럭 한대를 이용하여 다음의 조건을 모두 만족하면서 최대한 많은 박스들을 배송하려고 한다.

  • 조건 1: 박스를 트럭에 실으면, 이 박스는 받는 마을에서만 내린다.
  • 조건 2: 트럭은 지나온 마을로 되돌아가지 않는다.
  • 조건 3: 박스들 중 일부만 배송할 수도 있다.

마을의 개수, 트럭의 용량, 박스 정보(보내는 마을번호, 받는 마을번호, 박스 개수)가 주어질 때, 트럭 한 대로 배송할 수 있는 최대 박스 수를 구하는 프로그램을 작성하시오. 단, 받는 마을번호는 보내는 마을번호보다 항상 크다.

예를 들어, 트럭 용량이 40이고 보내는 박스들이 다음 표와 같다고 하자.

보내는 마을받는 마을박스 개수
1210
1320
1430
2310
2420
3420

이들 박스에 대하여 다음과 같이 배송하는 방법을 고려해 보자.

(1) 1번 마을에 도착하면

  • 다음과 같이 박스들을 트럭에 싣는다. (1번 마을에서 4번 마을로 보내는 박스는 30개 중 10개를 싣는다.)
보내는 마을받는 마을박스 개수
1210
1320
1430

(2) 2번 마을에 도착하면

  • 트럭에 실려진 박스들 중 받는 마을번호가 2인 박스 10개를 내려 배송한다. (이때 트럭에 남아있는 박스는 30개가 된다.)
  • 그리고 다음과 같이 박스들을 싣는다. (이때 트럭에 실려 있는 박스는 40개가 된다.)
보내는 마을받는 마을박스 개수
2310

(3) 3번 마을에 도착하면

  • 트럭에 실려진 박스들 중 받는 마을번호가 3인 박스 30개를 내려 배송한다. (이때 트럭에 남아있는 박스는 10개가 된다.)
  • 그리고 다음과 같이 박스들을 싣는다. (이때 트럭에 실려 있는 박스는 30개가 된다.)
보내는 마을받는 마을박스 개수
3420

(4) 4번 마을에 도착하면

  • 받는 마을번호가 4인 박스 30개를 내려 배송한다.

위와 같이 배송하면 배송한 전체 박스는 70개이다. 이는 배송할 수 있는 최대 박스 개수이다.

입력(Input)

입력의 첫 줄은 마을 수 N과 트럭의 용량 C가 빈칸을 사이에 두고 주어진다. N은 2이상 2,000이하 정수이고, C는 1이상 10,000이하 정수이다. 다음 줄에, 보내는 박스 정보의 개수 M이 주어진다. M은 1이상 10,000이하 정수이다. 다음 M개의 각 줄에 박스를 보내는 마을번호, 박스를 받는 마을번호, 보내는 박스 개수(1이상 10,000이하 정수)를 나타내는 양의 정수가 빈칸을 사이에 두고 주어진다. 박스를 받는 마을번호는 보내는 마을번호보다 크다.

출력(Output)

트럭 한 대로 배송할 수 있는 최대 박스 수를 한 줄에 출력한다.


서브태스크

번호배점제한
115보내는 마을번호는 모두 1, N ≤ 20
217받는 마을번호는 N-1 또는 N, 3 ≤ N ≤ 20
320N ≤ 5, M ≤ 5, C ≤ 10
423N ≤ 100, M ≤ 1,000, C ≤ 2,000
525추가적인 제약조건은 없다.

2. 문제 풀이

처음에 그냥 그리디적 가벼운 접근으로 해결하려고 하니 답이 나오지 않았다.
주어진 조건에서 택배의 거리로 짧은 순서대로 정렬하고 현 위치에서 싣을 수 있는 가장 많은 택배만큼 싣고 가려고 했었다.

그러나 그렇게 접근하니 예외처리를 해야하는 부분에서 오류가 발생하였다.

몇 시간을 헤매이다가 결국에는 힌트를 보고서 문제를 해결 할 수 있었다.

  1. 마을 별로 내리는 짐의 개수 대신에 K 마을에 방문해서 짐을 내린 후, 트럭에 싣고 있는 박스의 개수를 파악한다.
  2. 보내는 마을과 받는 마을의 거리가 멀수록 보내는 마을로부터 다음 마을들을 순차적으로 방문할 때마다 받는 마을 바로 직전의 마을까지 짐을 계속해서 들고 있어야 한다.
    • 그 사이에 더 큰 짐을 싣고 내리는 경우가 있을 수 있기 때문에 보내는 마을과 받는 마을의 거리가 멀면 그만큼 손해를 볼 수 있다.
  3. ‘무조건 1번 마을부터 N번 마을까지 순차적으로 방문한다는 조건’ & ‘보내는 마을보다 무조건 받는 마을의 번호가 높다는 조건’ 이 두 조건을 활용하는 것으로 문제를 풀 수 있다.

그렇다면 보내는 마을이 받는 마을과 거리가 가깝다는 뜻은 받는 마을의 번호를 오름차순으로 정렬했을 때 가장 앞에 있을수록 가깝다는 뜻이 된다.

그 다음 우선순위로는 보내는 마을을 오름차순으로 정렬하는 것이다.
** 두 번째 우선순위는 굳이 안해도 풀 수 있다.

즉! 아래의 단계로 해결 할 수 있었다.

  1. 받는 마을 번호를 오름차순으로 정렬
  2. 각 마을 별로 박스의 개수를 파악할 수 있는 Array 생성
  3. 앞에서부터 (보내는 마을, 받는 마을, 박스 개수) 를 꺼내서 사용
    ** 참고로, 받는 마을에서 박스 개수는 고려하지 않는다. 도착하면 박스를 내리기 때문에

2의 추가 내용

  • 정렬 후
보내는 마을받는 마을박스 개수
1210
2310
1320
3420
2420
1430

3) 추가 내용

  • 1번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 10 // 0 // 0 // 0 ( +10 내림 = 10)

  • 2번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 10 // 10 // 0 // 0 ( + 10 내림 = 20)

  • 3번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 30(+20) // 30(+20) // 0 // 0 ( + 20 내림 = 40)

  • 4번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 30 // 30 // 20(+ 20) // 0 ( + 20 내림 = 60)

  • 5번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 30 // 40(+10) // 30(+ 10) // 0 ( + 10 내림 = 70)
** 트럭의 용량이 40이기 때문에 2번 마을에 박스가 20개나 있음에도 불구하고 10만 싣을 수 있다.
** 3번 마을에는 2번에서 10만 싣었으니 당연히 10이 추가된다.

  • 6번 째 꺼낸 경우

트럭의 용량 : 40
마을 : 1 // 2 // 3 // 4
마을에서의 트럭에 싣은 박스 개수 : 30 // 40 // 30 // 0 ( + 0 내림 = 70)

따라서 최대 옮기는 박스의 개수는 70개가 된다.


3. 소스 코드

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.StringTokenizer;

public class ParcelService_8980 {
    public int solution(int n, int c, ArrayList<TownBoxes> townboxes) {
        Collections.sort(townboxes);

        int[] boxes = new int[n + 1];
        int boxCount = 0;
        for(TownBoxes town : townboxes) {
            int start = town.start;
            int end = town.end;
            int box = town.boxes;

            int max = 0;
            boolean isLoad = true;
            for(int i = start; i < end; i++) {
                if(boxes[i] >= c) {
                    isLoad = false;
                    break;
                }
                max = Math.max(boxes[i], max);
            }

            if(isLoad) {
                int dropOff = c - max;
                if(dropOff > box) {
                    dropOff = box;
                }
                boxCount += dropOff;
                
                for(int i = start; i < end; i++) {
                    boxes[i] += dropOff;
                }
            }
        }

        return boxCount;
    }
    public static void main(String[] args) throws IOException {
        ParcelService_8980 mc = new ParcelService_8980();
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        int N = Integer.parseInt(st.nextToken());
        int C = Integer.parseInt(st.nextToken());

        st = new StringTokenizer(br.readLine());
        int M = Integer.parseInt(st.nextToken());
        ArrayList<TownBoxes> boxes = new ArrayList<>();
        for (int i = 0; i < M; i++) {
            st = new StringTokenizer(br.readLine());
            int start = Integer.parseInt(st.nextToken());
            int end = Integer.parseInt(st.nextToken());
            int box = Integer.parseInt(st.nextToken());
            boxes.add(new TownBoxes(start, end, box));
        }

        System.out.println(mc.solution(N, C, boxes));
    }
}

class TownBoxes implements Comparable<TownBoxes> {
    int start = 0;
    int end = 0;
    int boxes = 0;
    TownBoxes(int start, int end, int boxes) {
        this.start = start;
        this.end = end;
        this.boxes = boxes;
    }

    @Override
    public int compareTo(TownBoxes box) {
        if(this.end == box.end) return this.start - box.start;
        else return this.end - box.end;
    }
}