포도주 시식

문제

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규칙이 있다.

  • 포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다.
  • 연속으로 놓여 있는 3잔을 모두 마실 수는 없다.

효주는 될 수 있는 대로 많은 양의 포도주를 맛보기 위해서 어떤 포도주 잔을 선택해야 할지 고민하고 있다. 1부터 n까지의 번호가 붙어 있는 n개의 포도주 잔이 순서대로 테이블 위에 놓여 있고, 각 포도주 잔에 들어있는 포도주의 양이 주어졌을 때, 효주를 도와 가장 많은 양의 포도주를 마실 수 있도록 하는 프로그램을 작성하시오.

예를 들어 6개의 포도주 잔이 있고, 각각의 잔에 순서대로 6, 10, 13, 9, 8, 1 만큼의 포도주가 들어 있을 때, 첫 번째, 두 번째, 네 번째, 다섯 번째 포도주 잔을 선택하면 총 포도주 양이 33으로 최대로 마실 수 있다.

입력

첫째 줄에 포도주 잔의 개수 n이 주어진다. (1≤n≤10,000) 둘째 줄부터 n+1번째 줄까지 포도주 잔에 들어있는 포도주의 양이 순서대로 주어진다. 포도주의 양은 1,000 이하의 음이 아닌 정수이다.

출력

첫째 줄에 최대로 마실 수 있는 포도주의 양을 출력한다.

예제 입력 1

6
6
10
13
9
8
1

예제 출력 1

33

출처

  • 빠진 조건을 찾은 사람: keith

알고리즘 분류

  • 다이나믹 프로그래밍

해결한 코드

var fs = require('fs');
var input = fs.readFileSync('/dev/stdin').toString().split('\n');    
for(var j=0; j<input.length; j++){
    input[j] = parseInt(input[j])
}
var n = input[0]
var wines = [...input]
var dp = new Array(n+1).fill(0)
dp[0] = 0
dp[1] = wines[1]
dp[2] = wines[1]+wines[2]
for(var i=3; i<=n; i++){
    dp[i] = Math.max(dp[i-2]+wines[i], dp[i-3]+wines[i-1]+wines[i], dp[i-1])
}
console.log(dp[n])

알고리즘

참고한 포스트 - [백준] 2156 - 포도주 시식 | 작성자 occidere
유사한 알고리즘 - 계단오르기

  1. 계단오르기 알고리즘처럼 최댓값을 구하여 dp 배열에 넣는다.
    ex. 포도주가 wine = [6, 10, 13, 9, 8, 1] 로 있다면
    1-1. 맨 처음 포도주를 마시는 최댓값: 6
    dp[1] = 6
    1-2. 두번째 포도주를 마시는 최댓값: 6+10
    dp[2] = 16 여기까지는 무리 없다.
    1-3. 세번째 포도주를 마시는 최댓값
    ① 전전포도주의 최댓값 + 현재포도주 : dp[3-2] + wine[3] = 19

    ② 전전전포도주의 최댓값 + 전포도주 + 현재포도주 : dp[3-3] + wine[3-1] + wine[3] = 0(?) + 10 + 13 = 23
    └그런데 현재 전전전포도주는 없지 않나?
    └ㅇㅇ 없다. 세번째 포도주에서 전전전포도주는 dp[0], 이땐 마신게 없으니 0으로 해준다.
    dp[0] = 0

    ③ 전포도주의 최댓값 : dp[3-1] = 16

    계단오르기와 달리 ※ 현재 포도주를 마시지 않을 수 있음을 주의하자 ※
    수가 0, 1과 같이 작은 수로 이루어지다가 뒤에 1000과 같은 큰 수가 오면 안마시는게 더 이득일 수 있다. 따라서 현재 포도주를 마시지 않는 경우까지 생각해서 최댓값을 구한다.
    이렇게 계산해주면 현재까지 dp는 dp=[0, 6, 16, 23]이 된다. 이 과정을 n번째까지 계속 반복한다.
  2. dp 배열의 마지막 원소를 출력한다.
    dp[n] = 33

이미 푸셨던 분들의 알고리즘만 보고 혼자서 해결하려고 해봤는데 계속 틀렸었다..
사실 나는 dp = [0, 6, 16, 23, 28, 33, 33]이 아니라 [6, 16, 23, 28, 33, 33]으로 들어가게끔 했다.

근데 흠.. for문으로 반복시켜주는 원소의 시작을 잘못 짰던 것 같기도 하다.

계단오르기처럼 dp[0], dp[1], dp[2] 까지 값을 계산해두고 dp[3]부터 n전까지 for문을 돌려가며 계산하는 걸로 짰는데, dp[2]에서 에러가 난게 아닐까 의심해본다.

dp[0]을 두고 (원래는 dp[2]에 해당했던) dp[3]부터 for문을 돌려 값을 계산했더니 통과했다.


2020-06-04 11:37
ㅋㅋㅋㅋㅋ 출력을 잘못했었다. 원래 코드도 맞았다.

(틀린줄 알았던)원래 코드

var fs = require('fs');
var input = fs.readFileSync('/dev/stdin').toString().split('\n');    
for(var j=0; j<input.length; j++){
    input[j] = parseInt(input[j])
}
var n = input[0]
var wines = input.slice(1, input.length)
var dp = new Array(n).fill(0) // dp를 n크기의 배열로 만들기
dp[0] = wines[0] // dp[0]에 바로 wines[0] 넣기 
dp[1] = wines[0]+wines[1]
dp[2] = Math.max(dp[0]+wines[2], wines[1]+wines[2], dp[1])
for(var i=3; i<n; i++){
    dp[i] = Math.max(dp[i-2]+wines[i], dp[i-3]+wines[i-1]+wines[i], dp[i-1])
}
console.log(dp[n-1])

굳이 dp[0] = 0으로 잡고, dp를 n+1 크기의 배열로 만들어주지 않고
dp[0] = wines[1]로 계산 시작하며, dp를 n 크기의 배열로 만들어줘도 계산 성공이다.

어쩐지 이상하다 했어.. 왜 안될까 했다구 ㅜㅜ

profile
개발 공부하는 심리학도

0개의 댓글