백준 풀기

백준 13305번: 주유소 with Java

삼겹살파튀 2024. 7. 2. 21:11

주유소 서브태스크

 
시간 제한메모리 제한제출정답맞힌 사람정답 비율
2 초 512 MB 70292 27545 21439 38.671%

문제

어떤 나라에 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 이하의 자연수이다. 

출력

표준 출력으로 제일 왼쪽 도시에서 제일 오른쪽 도시로 가는 최소 비용을 출력한다. 

서브태스크

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

 

 

처음 봤을 때: 엥? 이게 무슨 말임? 

 

난.. 컴과생이 맞나? 암튼 뭔 말인지도 모르겠더라구요

사실 읽지도 않고 그 생각 하고 걍 베린 거기도 함.

 

그럼 이제 읽어 볼게요. (진짜 지금 처음 읽음)

나라에 도시가 n개 있다 오호 서울 부산 광주 어쩌고~

오.. 제일 왼쪽에서 제일 오른쪽... 대충 서울에서 부산 간다고 칩시다... srt처럼 경유를 하는 거죠....  

서울에서 기름 넣고 출발 기름통 크기 무제한이라 기름 개많이 넣을 수 잇음 오호 1키로에 1리터 사용....

 

아하 저 선 위에 있는 숫자가 거리고 동그라미 안에 있는 숫자가 기름값이었습니다! 역시 서울이 기름이 비싸네요!

제일 싸게 끝까지 가는 값을 푸는 거였습니다 최저가 찾기~~

 

입력값 1: 도시 개수 == 동그라미 개수(2 <= n <= 100000)

입력값 2: 도로 길이 == 짝대기 위 숫자 순서대로(n-1개) 

입력값 3: 리터당 가격 == 동그라미 안의 숫자(n개)

 

근데 쓰다 보니까 기름을 많이 담아갈수록 차가 무거워지니까 사용하는 기름 양도 많아지는 거 아닌가 라는 의문이 조금 들긴 하는데요 더 복잡해지니까 그 생각은 접어두기로 합시다..

 

 

출력 부분까지 읽고

백준 초보는 서브태스크를 보고 당황하기 시작합니다.

이게 모지?????

 

근데 읽어보니까 중간고사 기말고사 부분 점수 같은 존재였음ㅇㅋㅇㅋ

 

그러면

1번부터 푸는 건가...?

일단 1번부터 풀어보기로...

-> 라는 건 백알못의 헛소리였습니다. 보니까 그냥 다 저 조건에 들어맞아야 한다는 것 같기도

 

모든 주유소의 리터당 가격은 1원이다. 지역 상관없이 공평한 1원? 대형마트에서 사나 봅니다.

2 <= n <= 1000이고 제일 왼쪽 도시부터 제일 오른쪽 도시까지의 거리는 최대 10,000, 리터당 가격은 최대 10,000이다...

 

뭔 소린지 모르겠고

일단 풀어볼게요!

 

 

갑자기 입력 부분부터 고민하게 됨

저 동그래미들은 배열로 받아야겟지 역시? 그러면 저 짝대기들도?

 

입력 부분을 만들고 뿌듯하게 있다가 최저가 알고리즘을 급하게 생각하기 시작했다.

어떻게 하면 최저값이냐구...? 기름을 제일 싸게 사는 게 최저가겠지요... 그러면 일단 비싼 곳에서는 도착지까지의 기름만 있어야 하고 싼 데서 최대한 많이 쟁여야 한다 근데 그걸 어떻게 아냐구??

 

1. 일단 맨 처음 기름을 넣긴 넣어야 한다 두 번째 갈 정도까지는...

2. 현재 있는 도시보다 바로 오른쪽 도시 기름값이 더 싸면 딱 갈 정도까지만 넣으면 되지 않나?

3. 오른쪽 도시 기름값이 더 비싸면? 그 다음 도시 갈 정도까지 넣어야겠지

4. 그 다음 도시도 기름값이 비싸면????? 

 

무한 비싸면의 굴레....

근데 생각해보면 또 맞는 것 같기도 하다.

이걸 코드로 어떻게 구현하느냐가 문제인데 그러면 기름값 비교를 한 담에 기름값 * 거리를 계속 total에 넣어주면 될라나??

일단 다시 짜볼게여

 

코드는 안 짜고

떡튀순오를 조지고 왔다.

다시 해봐야지

 

import java.util.Scanner;
public class BJ_13305 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		// 도시 개수
		int n = sc.nextInt();
		
		// 거리
		int[] length = new int[n - 1];
		for (int i = 0; i < n - 1; i++) {
			length[i] = sc.nextInt();
		}
		
		// 기름 가격
		int[] price = new int[n];
		for (int i = 0; i < n - 1; i++) {
			price[i] = sc.nextInt();
		}
		
		int total = 0;
		
		for (int i = 0; i < n - 1; i++) {
			if (price[i] >= price[i + 1]) {
				total += price[i] * length[i];
			}
			else {
				total += price[i] * (length[i] + length[i + 1]);
			}
		}
		
		System.out.println(total);
		
		sc.close();
	}
}

처음 짠 코드...

 

1차로 컴파일 에러가 나고(public 안 지움) 다시 돌리니 런타임 에러가 났다.

역시 너무 생각을 안 해서인가...????? 

 

 

main class Main Error: Could not find or load main class Main

 

뭐냐 넌

 

-> 클래스 이름 문제였다 Main으로 바꾸니까 정상 작동

 

하지만!! 17점이었다.ㅎ

 

 

그래.. 역시 생각을 안 해서 그래.... 이클립스 콘솔에 돌려봤는데 예제 1번부터 틀리더라

생각하다보니 무조건 최저가를 찾는다... 예전에 들었던 자료구조 수업이 떠오르면서 탐욕 알고리즘인가?! 라는 생각이

Greedy Algorithm~

 

 

import java.util.Scanner;
class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		// 도시 개수
		int n = sc.nextInt();
		
		// 거리
		int[] length = new int[n - 1];
		for (int i = 0; i < n - 1; i++) {
			length[i] = sc.nextInt();
		}
		
		// 기름 가격
		int[] price = new int[n];
		for (int i = 0; i < n - 1; i++) {
			price[i] = sc.nextInt();
		}
		
		int total = 0;
		
		for (int i = 0; i < n - 1; i++) {
			// 오른쪽 도시 기름값이 왼쪽 도시 기름값보다 쌀 때
			if (price[i] >= price[i + 1]) {
				total += price[i] * length[i];
			}
			else {
				total += price[i] * (length[i] + length[i + 1]);
				i++;
			}
		}
		
		System.out.println(total);
		
		sc.close();
	}
}

2차 수정

근데 똑같이 17점이다 쪼금 더 생각해서 한 칸 더 옆으로 보내줬는데...

뭐가 문제지???

 

 

하긴 1번 도시 기름값이 3번 도시 기름값보다 쌀 경우 이런 경우를 간과했다. 그럼 다시 수정....~~

그럼 최소값을 찾아야 하나??

 

기름 가격은 n개 받아야 하는데 n - 1개로 받고 있었다.. 17점이 나온 게 신기한 수준

 

 

import java.util.Scanner;
class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		// 도시 개수
		int n = sc.nextInt();
		
		// 거리
		int[] length = new int[n - 1];
		for (int i = 0; i < n - 1; i++) {
			length[i] = sc.nextInt();
		}
		
		// 기름 가격
		int[] price = new int[n];
		for (int i = 0; i < n; i++) {
			price[i] = sc.nextInt();
		}
		
		int total = 0;	// 비용 초기화
		int min = price[0];	// 일단 첫 번째 기름값을 min으로 잡음
		for (int i = 0; i < n - 1; i++) {
			if (price[i] < min) {
				min = price[i];	// 여기보다 싼 곳 있으면 min에 넣기
			}
			
			total += min * length[i];	// 최저가 가격으로 갈 수 있는 곳까지 가기
		}
		
		System.out.println(total);
		
		sc.close();
	}
}

 

그리디 알고리즘!!! 하면서 생각하니 오른쪽 하나하나 생각할 거 없이 최저가 찾으면 밀고나가다가 더 싼 곳 찾으면 그걸로 밀고 나간다 라고 짜니 생각보다 더 간단했다.

문제는

 

ㅎㅇ 나 58점....

서브태스크가 괜히 있는 게 아니구나 싶었다.

무서운 백준... 사람을 이렇게 간파하다니 

17 -> 58 차근차근 백준의 계획대로다.

 

2 ≤ N ≤ 1,000, 제일 왼쪽 도시부터 제일 오른쪽 도시까지의 거리는 최대 10,000, 리터 당 가격은 최대 10,000이다.

조건을 보니 타입 문제 같다 그러면 얘를 키워줘야지.. long 타입으로 바꿔줘야하나?!! 근데 java long 있는 건 아는데 long long 타입이 있는 지 갑자기 궁금해짐 -> 없대요

 

int 타입은 21억까지 되잖아요 근데 문제 보니까 거리 10억까지(총 길이) 기름은 리터당 가격이 10억까지도 되더라(이 정도면 세상 망한 듯) 그러면 총합이 21억 넘을 수도 있겠네..

귀찮아서 모든 걸 다 long 타입으로 바꿔버릴까 하다가 일단 쪼금만 바꿔보자 하고서 결과값 담는 total만 long으로 바꿨다가 또 58점을 받았다.

이유는 ... 뭘까?
암만 봐도 배열을 long으로 할 이유는 없는 것 같다. 10억이면 int로 충분히 커버되는 범위라 굳이 long을 쓸 필요가...?

한 번 고민하고 나니 절대 모든 걸 long으로 바꾸기 싫어졌다.

한참 생각해보다 total에 넣어주는 기름값 연산할 때가 문제인가?? 하고 그 부분도 타입 변환으로 고쳐줬다.

 

import java.util.Scanner;
class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		
		// 도시 개수
		int n = sc.nextInt();
		
		// 거리
		int[] length = new int[n - 1];
		for (int i = 0; i < n - 1; i++) {
			length[i] = sc.nextInt();
		}
		
		// 기름 가격
		int[] price = new int[n];
		for (int i = 0; i < n; i++) {
			price[i] = sc.nextInt();
		}
		
		long total = 0;	// 비용 초기화, 21억 넘을 수도 있으니까 long 타입
		int min = price[0];	// 일단 첫 번째 기름값을 min으로 잡음
		for (int i = 0; i < n - 1; i++) {
			if (price[i] < min) {
				min = price[i];	// 여기보다 싼 곳 있으면 min에 넣기
			}
			
			total += (long) min * length[i];	// 최저가 가격으로 갈 수 있는 곳까지 가기, 곱했을 때 int 초과할 수도 있으니까 타입 변환
		}
		
		System.out.println(total);
		
		sc.close();
	}
}

 

total만 long 타입으로 변환

total에 더해줄 때 우변 (long) 변환 추가

 

결과는?

 

오예~

승리(ㅂㅂX)다 이거야~

 

 

오늘의 결론

1. 문제를 잘 읽자 괜히 교수님이 문제 읽기가 제일 중요하다는 말을 하신 게 아님

2. 백준의 서브태스크는.. 괜히 있는 게 아니구나

3. 처음 생각을 잘 하자

 

끝!