문제
어떤 나라에 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 | 원래의 제약조건 이외에 아무 제약조건이 없다. |
풀이
먼저 가격이 제일 싼 주유소를 찾은 뒤, 그 주유소가 나오기 전까지는 각 주유소에서 다음 주유소로 이동할 만큼만 기름을 구매하면서 가다가 가격이 제일 싼 주유소를 찾으면 남은 거리만큼 기름을 전부 사는 로직을 생각하였다.첫 번째 코드
import sys
s = list(map(int, sys.stdin.read().split()))
n = s[0]
km = s[1:n]
won = s[n:]
cheap = min(s[n:-1])
cost = 0
for i in range(n):
if won[i] != cheap:
cost += km[i] * won[i]
else:
cost += cheap * sum(km[i:])
break
print(cost)

예시 입출력이 다 잘 돌아가길래 당연히 한 번에 맞을 거라고 생각했는데, 서브태스크는 만족시키지 못했다. 원인이 뭔지 찾아보았다.
내가 한 실수는 제일 싼 주유소 하나만 고려하고 두번째, 세번째... 로 싼 주유소는 고려하지 못한 것이었다.
예를 들어 각 주유소의 비용이 [10, 6, 8, 4, 1], 거리는 [2, 2, 2, 2]라고 가정하자.
내가 구현한 로직대로라면
10 : 최솟값 아님 => 10 x 2 = 20
6 : 최솟값 아님 => 6 x 2 = 12
8 : 최솟값 아님 => 8 x 2 = 16
4 : 최솟값 발견 => 4 x 2 = 8
=> 총 비용 20 + 12 + 16 + 8 = 56
그러나 6 주유소에 도착했을 때 8->4까지 이동하는 기름값도 결제한다면 4원 절약할 수 있다.
10 : 최솟값 아님 => 10 x 2 = 20
6 : 8까지 결제 => 6 x 4 = 24
8 : 이미 결제함 => 0
4 : 최솟값 발견 => 4 x 2 = 8
=> 총 비용 20 + 24 + 8 = 52
즉, 이 문제는 그리디 알고리즘(Greedy Algorithm)을 활용하여 푸는 문제인 것이다.
Greedy Algorithm
: 매 선택에서 지금 이 순간 당장 최적인 답을 선택하는 알고리즘
1. 전체 주유소에서 하나의 최솟값을 찾는 대신 그때그때 지금까지 지나온 주유소 중 최솟값을 저장&갱신
: 기존의 min()을 활용하여 사전에 주유소의 최소비용을 찾고 시작하는 방법 대신, 최솟값을 맨 처음엔 첫 주유소 비용으로 설정해둔 뒤 for문 내에서 더 작은 값을 만날 때마다 최솟값을 갱신하고 그 최솟값으로 기름을 구매한다.
최종 코드
import sys
s = list(map(int, sys.stdin.read().split()))
n = s[0]
km = s[1:n]
won = s[n:]
cheap = s[n]
cost = 0
for i in range(n - 1):
if won[i] < cheap:
cheap = won[i]
cost += cheap * km[i]
print(cost)
코드도 훨씬 간결해졌고, 한 번에 100점 맞는데 성공했다 ㅎㅎ
배운 것
- sys.stdin.readline() : 파일의 내용을 한 번에 읽어온다. 지금같이 최대 데이터가 10만개 정도일 때는 괜찮지만, 100만개 정도로 데이터가 더 많아지면 한 줄씩 읽는 sys.stdin.read().split()을 사용하는 것이 속도가 더 빠를 수 있다고 한다.
'[Python] 백준 알고리즘 공부' 카테고리의 다른 글
| [Python] 2512. 예산 (0) | 2026.03.08 |
|---|---|
| [Python] 20920. 영단어 암기는 괴로워 (0) | 2026.03.07 |
| [Python] 2164. 카드2 (0) | 2026.03.06 |
| [Python] 17266. 어두운 굴다리 (0) | 2026.03.01 |
| [Python] 9017. 크로스 컨트리 (0) | 2026.02.21 |