백준

Q5 주유소(13305번 문제)

mozzi329 2022. 7. 11. 00:58
728x90

 

     

    📌 문제

    문제)
    어떤 나라에 N개의 도시가 있다. 이 도시들은 일직선 도로 위에 있다. 편의상 일직선을 수평 방향으로 두자. 제일 왼쪽의 도시에서 제일 오른쪽의 도시로 자동차를 이용하여 이동하려고 한다. 인접한 두 도시 사이의 도로들은 서로 길이가 다를 수 있다. 도로 길이의 단위는 km를 사용한다. 처음 출발할 때 자동차에는 기름이 없어서 주유소에서 기름을 넣고 출발하여야 한다. 기름통의 크기는 무제한이어서 얼마든지 많은 기름을 넣을 수 있다. 도로를 이용하여 이동할 때 1km마다 1리터의 기름을 사용한다. 각 도시에는 단 하나의 주유소가 있으며, 도시마다 주유소의 리터당 가격은 다를 수 있다. 가격의 단위는 원을 사용한다.

    예를 들어, 이 나라에 다음 그림처럼 4개의 도시가 있다고 하자.
    원 안에 있는 숫자는 그 도시에 있는 주유소의 리터당 가격이다. 도로 위에 있는 숫자는 도로의 길이를 표시한 것이다.제일 왼쪽 도시에서 6리터의 기름을 넣고, 더 이상의 주유 없이 제일 오른쪽 도시까지 이동하면 총 비용은 30원이다. 만약 제일 왼쪽 도시에서 2리터의 기름을 넣고(2×5 = 10원) 다음 번 도시까지 이동한 후 3리터의 기름을 넣고(3×2 = 6원) 다음 도시에서 1리터의 기름을 넣어(1×4 = 4원) 제일 오른쪽 도시로 이동하면, 총 비용은 20원이다. 또 다른 방법으로 제일 왼쪽 도시에서 2리터의 기름을 넣고(2×5 = 10원) 다음 번 도시까지 이동한 후 4리터의 기름을 넣고(4×2 = 8원) 제일 오른쪽 도시까지 이동하면, 총 비용은 18원이다.각 도시에 있는 주유소의 기름 가격과, 각 도시를 연결하는 도로의 길이를 입력으로 받아 제일 왼쪽 도시에서 제일 오른쪽 도시로 이동하는 최소의 비용을 계산하는 프로그램을 작성하시오.
    입력)
    표준 입력으로 다음 정보가 주어진다.
    첫 번째 줄에는 도시의 개수를 나타내는 정수 N(2 ≤ N ≤ 100,000)이 주어진다.
    다음 줄에는 인접한 두 도시를 연결하는 도로의 길이가 제일 왼쪽 도로부터 N-1개의 자연수로 주어진다.
    다음 줄에는 주유소의 리터당 가격이 제일 왼쪽 도시부터 순서대로 N개의 자연수로 주어진다.

    제일 왼쪽 도시부터 제일 오른쪽 도시까지의 거리는 1이상 1,000,000,000 이하의 자연수이다.
    리터당 가격은 1 이상 1,000,000,000 이하의 자연수이다.  
    출력)
    표준 출력으로 제일 왼쪽 도시에서 제일 오른쪽 도시로 가는 최소 비용을 출력한다.

    ✔️ 서브태스크

    점수 설명
    17점 모든 주유소의 리터당 가격은 1원이다.
    58점 2 ≤ N ≤ 1,000
    제일 왼쪽 도시부터 제일 오른쪽 도시까지의 거리는 최대 10,000, 리터 당 가격은 최대 10,000이다.
    100점 원래의 제약조건 이외에 아무 제약조건이 없다.

     

    📌 문제 분석 ❗️

    ❗️ 가장 끝 도시는 제외한다.

    가장 끝 도시는 제외한다

    주어진 문제가 제일 왼쪽 도시에서 제일 오른쪽 도시로 가는 최소 비용을 출력하는 문제이기 때문에 마지막 도시에 도착했을 때, 마지막 도시의 기름 가격이 얼마이던지 주유를 하지 않는다. 그래서 제일 마지막 도시 자체는 고려하지 않는다. 

     

    ❗️ 구간의 주유비용 = 해당 도시의 리터당 주유비용(리터당 1km) * 다음 도시의 거리이다.

    다음 도시의 거리는 도시에 도착할 때마다 항상 변하지만 해당 도시의 기름 가격은 전 도시의 가격과 비교해야한다.

     

    ❗️ 이전 도시의 주유비용보다 도착한 도시의 주유비용이 더 저렴한 경우 가격을 변경해준다.

    결국 도시의 구간마다 최소의 주유비용을 선택하여 최선의 주유비용을 구하는 문제이므로 그리디 알고리즘을 통해 최적해를 구할 수 있다.

     

    ❗️ 서브태스크를 만족하기 위해 long타입으로 변수를 선언해준다.

    서브태스크 3번에 원래의 제약 조건(거리 : 1이상 1,000,000,000 이하의 자연수, 주유비용 : 1이상 1,000,000,000 이하의 자연수)을 만족하려면 long 타입의 변수를 사용해주어야 한다.

     

    📌 선언한 변수

    변수 종류 변수 명 설명
    BufferedReader br 입력 값을 받기 위한 버퍼 함수 선언
    int numOfCity 도시의 개수
    StringTokenizer endTokenizer 받은 입력 값을 공백 기준으로 도시 간 거리 값을 분리
    StringTokenizer oilPriceTokenizer 받은 입력 값을 공백 기준으로 도시별 주유비욜 값을 분리
    ArrayList<City> cityList City 객체를 다루기 위한 City 타입의 ArrayList 선언
    City(클래스) x long 타입의 주유 비용과 도시간의 거리를 담을 수 있는 클래스를 선언해준다.
    long dist City 객체의 도시간 거리값을 담을 수 있는 long 타입 변수를 선언한다.
    long price City 객체의 주유비용을 담을 수 있는 long 타입 변수를 선언한다.
    long totalPrice 구간의 주유비용을 누적한 값을 담을 long 타입 변수를 선언한다. (최종 출력)
    long d, p StringTokenizer에서 분리된 토큰을 City 객체에 담기위한 임시 변수(거리, 주유비)

     

    📌 진행 

    ❗️반복문을 통해 d(거리), p(주유비)를 넘겨 City객체를 생성하고 ArrayList에 객체를 저장한다.

            for (int i = 0; i < numOfCity - 1; i++) {
                d = Long.valueOf(endKmTokenizer.nextToken());
                p = Long.valueOf(oilPriceTokenizer.nextToken());
                cityList.add(new City(d, p));
            }

    numOfCity - 1 : 마지막 도시는 고려할 필요없다.

     

    ❗️처음 도시에서는 무조건 주유해야 한다. = 초기 값에 첫 인덱스를 넣는다.

            price = cityList.get(0).price;
            dist = cityList.get(0).distance;
            totalPrice = price * dist;

     

    차가 처음 출발하기 위해선 무조건 기름을 주유해야 한다. 그래서 price(주유비)와 dist(도시 간 거리), totalPrice(구간 별 주유비 누적 값)는 0번 인덱스의 값이 되어야 한다.

     

    ❗️다음 도시에 도착했을 때 주유비의 가격이 이전 도시의 주유비 가격보다 작을 경우 price의 값을 변경해준다.

            for (int i = 1; i < numOfCity - 1; i++) {
    
                if (cityList.get(i).price < price) {
                    price = cityList.get(i).price;
                }
                dist = cityList.get(i).distance;
                totalPrice += price * dist;
            }

    변경된 주유비의 값(조건에 따라 바뀜)과 도시 간 거리(구간마다 계속 바뀜)를 곱해 totalPrice에 계산된 값을 누적해준다. 그 후 totalPrice의 값을 출력해준다.

     

    📌 전체 코드

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.*;
    
    public class Main {
    
        public static void main(String[] args) throws IOException {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            int numOfCity = Integer.parseInt(br.readLine());
            StringTokenizer endKmTokenizer = new StringTokenizer(br.readLine());
            StringTokenizer oilPriceTokenizer = new StringTokenizer(br.readLine());
    
            ArrayList<City> cityList = new ArrayList<>();
            long dist = 0L;
            long price = 0L;
            long totalPrice = 0L;
            long d,p;
    
            for (int i = 0; i < numOfCity - 1; i++) {
                d = Long.valueOf(endKmTokenizer.nextToken());
                p = Long.valueOf(oilPriceTokenizer.nextToken());
                cityList.add(new City(d, p));
            }
    
            price = cityList.get(0).price;
            dist = cityList.get(0).distance;
            totalPrice = price * dist;
    
            for (int i = 1; i < numOfCity - 1; i++) {
    
                if (cityList.get(i).price < price) {
                    price = cityList.get(i).price;
                }
                dist = cityList.get(i).distance;
                totalPrice += price * dist;
            }
            System.out.println(totalPrice);
        }
    }
    
    class City {
        long distance = 0;
        long price = 0;
    
        public City (long distance, long price) {
            this.distance = distance;
            this.price = price;
        }
    }

     

    📌 참조

    그리디 알고리즘(Greedy Algorithm) : 욕심쟁이 알고리즘(클릭)