[C++] 백준 2156 [포도주 시식]

date:

Updated:


Categories:

Tags: , ,

문제설명

수열의 원소 N개 중 합이 최대가 되도록 원소를 선택하되, 연속된 3개의 수는 선택하지 못한다.
선택한 원소들의 합을 출력한다.

풀이방법

Top-down 풀이방식이다.
Cache array는 이중 배열로 구현하였다.
DP(a, b)의 정의 : b번째 원소까지의 연속된 3개의 수를 선택하지 않은 원소들의 합의 최대값(단, b번째 원소 이후로 b와 연속된 원소의 개수는 a개)
grape[b] : b번째 원소

점화식

\(DP(0, b) = max(DP(0, b-1), DP(1, b-1) + grape[b]\)
\(DP(1, b) = max(DP(0, b-1), DP(2, b-1) + grape[b]\)
\(DP(2, b) = DP(0, b-1)\)

문제의 답은 DP(0, N)이다. N번째 원소가 마지막이기 때문에, a가 0인 경우만 가능하다.
DP(0, b)은 원소 n을 선택하거나, 선택하지 않을 수 있다.
만약 선택한다면, b-1번째 원소는 b과 연속하기 때문에 DP(1, b-1)으로, a가 1이 된다. 그러므로 grape[b] + DP(1, b-1)이다.
만약 선택하지 않는다면 연속하지 않기 때문에 DP(0, b-1)이 된다.
우리는 최대값을 찾아야 하므로, max(DP(0, b-1), DP(1, b-1) + grape[b])이 된다.
DP(1, b)의 경우, b+1번째 값을 선택한 상태이므로, 현재 원소를 선택하게 되면 grape[b] + DP(2, b-1)이 되고, 선택하지 않으면 DP(0, b-1)이 된다.
그러므로 max(DP(0, b-1), DP(2, b-1) + grape[b])가 된다.
DP(2, b)의 경우, b+1번째와 b+2번째 값을 선택한 상태이므로, 현재 원소를 선택할 수 없어서 DP(0, b-1)이 된다.
이 세개의 점화식을 활용하여 아래와 같이 코드를 작성할 수 있다.

소스코드

#include<iostream>
#include<vector>
#include<cstring>

using namespace std;

vector<int> grape;
int cache[3][10001];

int DP(int a, int b){
    if(a == 2 && b == 1) return 0;
    if(b == 1) return grape[0];

    int& ret = cache[a][b];
    if(ret != -1) return ret;

    if(a == 0) ret = max(DP(0, b-1) ,DP(1, b-1) + grape[b-1]);
    if(a == 1) ret = max(DP(0, b-1) ,DP(2, b-1) + grape[b-1]);
    if(a == 2) ret = DP(0, b-1);

    return ret;
}

int main(){
    int N;
    cin >> N;
    memset(cache, -1, sizeof(cache));
    for(int i=0; i<N; i++){
        int n;
        cin >> n;
        grape.push_back(n);
    }
    cout << DP(0, N);
}

Leave a comment