문제
효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규칙이 있다.
- 포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다.
- 연속으로 놓여 있는 3잔을 모두 마실 수는 없다.
효주는 될 수 있는 대로 많은 양의 포도주를 맛보기 위해서 어떤 포도주 잔을 선택해야 할지 고민하고 있다. 1부터 n까지의 번호가 붙어 있는 n개의 포도주 잔이 순서대로 테이블 위에 놓여 있고, 각 포도주 잔에 들어있는 포도주의 양이 주어졌을 때, 효주를 도와 가장 많은 양의 포도주를 마실 수 있도록 하는 프로그램을 작성하시오.
예를 들어 6개의 포도주 잔이 있고, 각각의 잔에 순서대로 6, 10, 13, 9, 8, 1 만큼의 포도주가 들어 있을 때, 첫 번째, 두 번째, 네 번째, 다섯 번째 포도주 잔을 선택하면 총 포도주 양이 33으로 최대로 마실 수 있다.
입력
첫째 줄에 포도주 잔의 개수 n이 주어진다. (1 ≤ n ≤ 10,000) 둘째 줄부터 n+1번째 줄까지 포도주 잔에 들어있는 포도주의 양이 순서대로 주어진다. 포도주의 양은 1,000 이하의 음이 아닌 정수이다.
출력
첫째 줄에 최대로 마실 수 있는 포도주의 양을 출력한다.
풀이
계단 문제와 유사하다. 3개의 항을 연속해서 선택할 수 없다는 제한조건이 있으며 최대값을 골라내야 한다.
경우의 수를 그림으로 표현해본다. 끝 항 4개를 기준으로 경우의 수를 나눠본다
N-2항 | N-1항 | N항 |
X | O | O |
O | X | O |
X / O | O | X |
N항을 선택하는 경우가 있고 아닌 경우가 존재한다.
선택했을 때, 점프를 해야만하는 N-1항과 점프를 하지 않아도 되는 N-2항을 비교해 더 큰 값을 가져온다
→ dp[N][점프 선택 가능] = Math.max( dp[N-1][점프 필수], dp[N-2][점프 선택 가능]);
선택하지 않았을 때, 점프를 할 수도 있고 안할 수도 있는 N-1항을 가져온다
→ dp[N][선택하지 않음] = dp[N-1][ 점프 선택 가능 ]
N=6
100 400 2 1 4 200 을 입력한다 → 위의 논리대로라면 701이 나오지만 실제 답은 704이다.
200과 4를 선택하고 두번을 건너 뛰었기 때문이다. 조건을 하나 더 추가해준다.
선택하지 않았을 때, 한 번 더 건너뛸 수 있도록 한다
→ dp[N][선택하지 않음] = Math.max( dp[N-1][ 점프 선택 가능 ], dp[N-2][ 점프 선택 가능 ] )
import java.util.Scanner;
public class Main {
static Integer dp[][];
static int[] wine;
static Integer drink(int N, int C) {
if(N<=0)return dp[0][C];
else if(dp[N][0]==null || dp[N][1]==null || dp[N][2]==null) {
dp[N][0] = Math.max(drink(N-1, 2),Math.max(drink(N-1,1)+wine[N-1],drink(N-2,0)+wine[N-1]));
dp[N][1] = Math.max(drink(N-2,0)+wine[N-1], drink(N-2,2)+wine[N-1]);
dp[N][2] = Math.max(drink(N-1,0), drink(N-2,0));
// 0은 점프 선택가능한 경우: 점프를 한 결과와 하지 않은 결과 중 최대값을 가져온다
// 1은 점프 필수: 점프를 한 값을 가져온다
// 2는 선택하지 않음: 반례를 고려해 전전항으로 보낼 수 있도록 한다
}
return dp[N][C];
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
wine = new int[n];
dp = new Integer[n+1][3];
//0은 붙어있지 않아 점프 선택 가능, 1은 붙어있어 점프필수, 2는 선택되지 않았을 때
for(int i=0;i<n;i++){
wine[i]=sc.nextInt();
} //배열에 입력값을 집어넣는다
int max=0;
dp[0][0]=0; dp[0][1]=0; dp[0][2]=0;
for(int i=0; i<3; i++)
max=Math.max(max, drink(n,i));
System.out.print(max);
//셋 중 최대값 출력
}
}
'Study > Baekjoon' 카테고리의 다른 글
Baekjoon1463*: 1로 만들기 (0) | 2021.10.30 |
---|---|
Baekjoon1932: 정수 삼각형 (0) | 2021.10.30 |
Baekjoon2565: 전깃줄 (0) | 2021.10.28 |
Baekjoon2579: 계단 오르기 (0) | 2021.10.28 |
Baekjoon1149: RGB거리 (0) | 2021.10.28 |