문제:

기차는 맨 앞에 있는 기관차 1대가 손님이 탄 객차 여러 칸을 끌고 간다. 기관차가 고장나면 기차를 운행할 수 없게 되므로 최근 철도청은 기관차 고장에 대비하여 몇몇 역에 소형 기관차 3대를 배치하기로 결정하였다. 소형 기관차는 평소에 이용하는 기관차보다 훨씬 적은 수의 객차만을 끌 수 있다.

기관차가 고장났을 때 끌고 가던 객차 모두를 소형 기관차 3대가 나누어 끌 수 없기 때문에, 소형 기관차들이 어떤 객차들을 끌고 가는 것이 좋을까하는 문제를 고민하다가 다음과 같이 하기로 결정하였다.

  1. 소형 기관차가 최대로 끌 수 있는 객차의 수를 미리 정해 놓고, 그보다 많은 수의 객차를 절대로 끌게 하지 않는다. 3대의 소형 기관차가 최대로 끌 수 있는 객차의 수는 서로 같다.
  2. 소형 기관차 3대를 이용하여 최대한 많은 손님을 목적지까지 운송하도록 한다. 각 객차 마다 타고 있는 손님의 수는 미리 알고 있고, 다른 객차로 손님들이 이동하는 것은 허용하지 않는다.
  3. 각 소형 기관차는 번호가 연속적으로 이어진 객차를 끌게 한다. 객차는 기관차 바로 뒤에 있는 객차부터 시작하여 1번 부터 차례로 번호가 붙어있다.

예를 들어 기관차가 끌고 가던 객차가 7칸이고, 소형 기관차 1대가 최대로 끌 수 있는 객차 수는 2칸이라고 하자. 그리고 1번 부터 7번까지 각 객차에 타고 있는 손님의 수가 아래 표와 같다고 하자. 괄호속에 있는 숫자는 객차 번호를 나타낸다.

(1)(2)(3)(4)(5)(6)(7)

35 40 50 10 30 45 60

소형 기관차 3대는 각각 1-2번, 3-4번, 그리고 6-7번 객차를 끌고 가면 손님 240명을 운송할 수 있고, 이보다 많은 수의 손님을 운송할 수 없다.

기관차가 끌고 가던 객차의 수와 각 객차에 타고 있던 손님의 수, 그리고 소형 기관차가 최대로 끌수 있는 객차의 수가 주어질 때, 소형 기관차 3대를 이용하여 최대로 운송할 수 있는 손님 수를 구하는 프로그램을 작성하시오.

입력:

첫째 줄에 기관차가 끌고 가던 객차의 수가 입력된다. 그 수는 50,000 이하이다. 둘째 줄에는 기관차가 끌고 가던 객차에 타고 있는 손님의 수가 1번 객차부터 차례로 입력된다. 한 객차에 타고 있는 손님의 수는 100명 이하이고, 입력되는 숫자들 사이에 빈칸이 하나씩 있다. 셋째 줄에는 소형 기관차가 최대로 끌 수 있는 객차의 수가 입력된다. 그 수는 기관차가 끌고 가던 객차 수의 1/3보다 적다.

출력:

한 줄에 소형 기관차 3대를 이용하여 최대로 운송할 수 있는 손님 수를 출력한다.

풀이방법:

점화식을 생성해서 DP로 해당 문제를 풀 수 있다. DP는 다음과 같이 정의한다.

  • DP[N][M]: 소형기관차 N대를 운행할 때 M번째 객차를 선택했을 때 최대로 운송 가능한 손님 수

따라서 점화식은 다음과 같다.

 

DP[N][M] = max( DP[N][M-1], DP[N-1][M-limit] + sum(numbers[M-limit+1:M+1]))

 

점화식을 사용해서 문제의 예제에 대입하면 다음과 같이 DP 테이블을 채워나갈 수 있다.

객차 번호 1 2 3 4 5 6 7
손님 수  35 40 50 10 30 45 60
1   35+40 40+50 90 90 90 45+60
2       75+60 135 90 + 75 90 + 105
3           75+60+75 75+60+105

예시를 참고하여 어떠한 원리로 동작하는지 알아보도록 한다.

  • DP[1][2] = max(DP[1][1], DP[0][2-2] + sum(numbers[2-2+1:2+1]))
    • DP[1][1], DP[0][0]는 둘 다 0이기때문에, sum만 계산하면 되므로 35+45다.
  • DP[3][6] = max(DP[3][5], DP[2][6-2] + sum(numbers[6-2+1:6+1])
    • DP[3][5]는 0이므로, 뒷 항에 의해 결된다. DP[2][6-2]는 75+60이고, 여기에 30+45를 더해준다.

점화식이 의미하는 것은 현재 운행하고 있는 소형 기관차가 이전의 객차를 변경없이 유지했을 때(DP(N][M-1]), N번째 소형 기관차를 객차를 선택하기 전에 N-1번째 소형기관차를 유지하면서(DP[N-1][M-limit]) N번째 소형기관차가 M번째 소형기관차를 선택했을 때(sum(numbers[M-limit+1:M+1]))를 비교하는 것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
import sys
input = sys.stdin.readline
 
= int(input())
numbers = [0]+ list(map(int,input().split()))
= int(input())
dp = [[0 for _ in range(N+1)] for _ in range(4)]
for i in range(1,4):
    for j in range(M*i, N+1):
        dp[i][j] = max(dp[i][j-1], dp[i-1][j-M]+sum(numbers[j-M+1:j+1]))
        
print(dp[3][N])
cs

문제링크:

https://www.acmicpc.net/problem/2616

 

2616번: 소형기관차

첫째 줄에 기관차가 끌고 가던 객차의 수가 입력된다. 그 수는 50,000 이하이다. 둘째 줄에는 기관차가 끌고 가던 객차에 타고 있는 손님의 수가 1번 객차부터 차례로 입력된다. 한 객차에 타고 있

www.acmicpc.net

 

'Algorithm > Python' 카테고리의 다른 글

[BOJ]10834. 벨트  (0) 2022.05.26
[BOJ]8981. 입력숫자  (0) 2022.05.24
[BOJ]2550. 전구  (0) 2022.05.17
[BOJ]2116. 주사위 쌓기  (0) 2022.05.12
[BOJ]1268. 임시 반장 정하기  (0) 2022.05.10

문제:

개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이 번갈아가면서 등장한다.

아래 그림은 길이가 14미터이고 높이가 5미터인 동굴이다. (예제 그림)

이 개똥벌레는 장애물을 피하지 않는다. 자신이 지나갈 구간을 정한 다음 일직선으로 지나가면서 만나는 모든 장애물을 파괴한다.

위의 그림에서 4번째 구간으로 개똥벌레가 날아간다면 파괴해야하는 장애물의 수는 총 여덟개이다. (4번째 구간은 길이가 3인 석순과 길이가 4인 석순의 중간지점을 말한다)

하지만, 첫 번째 구간이나 다섯 번째 구간으로 날아간다면 개똥벌레는 장애물 일곱개만 파괴하면 된다.

동굴의 크기와 높이, 모든 장애물의 크기가 주어진다. 이때, 개똥벌레가 파괴해야하는 장애물의 최솟값과 그러한 구간이 총 몇 개 있는지 구하는 프로그램을 작성하시오.

입력:

첫째 줄에 N과 H가 주어진다. N은 항상 짝수이다. (2 ≤ N ≤ 200,000, 2 ≤ H ≤ 500,000)

다음 N개 줄에는 장애물의 크기가 순서대로 주어진다. 장애물의 크기는 H보다 작은 양수이다.

출력:

첫째 줄에 개똥벌레가 파괴해야 하는 장애물의 최솟값과 그러한 구간의 수를 공백으로 구분하여 출력한다.

풀이방법:

이분 탐색을 사용하여, 빠르게 인덱스를 찾도록 한다. 석순과 종유석을 각자의 배열에 담고, 이들을 정렬한다. 그리고 H를 늘려가며, 부셔야 하는 갯수를 찾는데, 개똥벌레는 정수의 높이로 날라가는 것이 아닌 0.5 지점을 날기 때문에 이를 고려하여 인덱스를 찾아야 한다. 종유석과 석순 개별적으로 부셔야 하는 갯수를 찾은 뒤 이들의 합이 가장 작을 때를 찾도록 한다.

 만약 최솟값이 여러번 나오는 경우에는 이를 누적해서 더해주고, 만약 새로운 최솟값이 나오면 누적 합을 초기화시켜준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import bisect
import sys
 
input=sys.stdin.readline
N, H = map(int,input().split())
min_count = N
range_answer = 0
 
obstacle_d = []
obstacle_u = []
 
for i in range(N):
    if i%2:
        obstacle_u.append(int(input()))
    else:
        obstacle_d.append(int(input()))
 
obstacle_d.sort()
obstacle_u.sort()
 
for i in range(1, H+1):
    down_count = len(obstacle_d) - bisect.bisect_left(obstacle_d, i-0.5)
    up_count = len(obstacle_u) - bisect.bisect_left(obstacle_u, H-(i-0.5))
    if min_count == down_count+up_count:
        range_answer+=1
    elif min_count > down_count+up_count:
        min_count = down_count+up_count
        range_answer = 1
 
        print(min_count,range_answer)
cs

문제링크:

https://www.acmicpc.net/problem/3020

 

3020번: 개똥벌레

개똥벌레 한 마리가 장애물(석순과 종유석)로 가득찬 동굴에 들어갔다. 동굴의 길이는 N미터이고, 높이는 H미터이다. (N은 짝수) 첫 번째 장애물은 항상 석순이고, 그 다음에는 종유석과 석순이

www.acmicpc.net

 

'Algorithm > Python' 카테고리의 다른 글

[BOJ] 10157. 자리배정  (0) 2021.10.05
[BOJ] 1019. 책 페이지  (0) 2021.10.01
[BOJ]6593. 상범 빌딩  (0) 2021.09.29
[BOJ]12904. A와 B  (0) 2021.09.28
[BOJ]1722. 순열의 순서  (0) 2021.09.27

+ Recent posts