선생님, 개발을 잘하고 싶어요.

백준 2156번 포도주 시식 본문

개발/알고리즘 공부

백준 2156번 포도주 시식

알고싶은 승민 2019. 4. 22. 23:42

문제링크 : https://www.acmicpc.net/problem/2156

 

2156번: 포도주 시식

효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규칙이 있다. 포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다. 연속으로 놓여 있는 3잔을 모두 마실 수는 없다. 효주는 될 수 있는 대로 많은 양의 포도주를 맛보기 위해서 어떤 포도주 잔을 선택해야 할지 고

www.acmicpc.net

오랜만에 백준 문제풀이잼

 

다이나믹 프로그래밍 적용하면 풀 수 있는 간단한 문제이다.

이 문제는 함수 하나만 제작하면 된다. 바로 그것은

 

i-1이 안먹은 상태일 때, 효주가 i번째 잔 부터 가장 많이 먹을 수 있는 포도주의 양

 

을 반환하는 함수를 생각해내면 문제가 아주 쉽다. 이 함수를 P(i), i번째 포도주의 양을 A(i) 라고 해보자.

 

연속으로 3잔을 모두 마실 수 없기때문에 바로 앞의 잔을 안먹은 상태라면 다음과 같은 상황이 있을 수 있다.

 

i번째 잔을 안 먹었을 때 ---> i번째 잔을 안먹은 상태이기 때문에, P(i+1)이 가장 많이 먹을 수 있는 포도주의 양

 

i번째 잔을 먹었을 때는 다시 두가지 경우로 나뉜다.

    i+1번째 잔을 안 먹었을 때 ---> A(i) + i+1번째 잔을 안먹은 상태이기 때문에, P(i+2)

    i+1번째 잔을 먹었을 때 ---> A(i) + A(i+1) + i+2번째 잔은 못먹기 때문에, P(i+3)

 

그러니까 결국 위의 3가지 상황중 가장 큰 값이 i번째 잔 부터 효주가 가장 많이 먹을 수 있는 포도주의 양 이다.

 

이제 이 상황을 프로그래밍 해주면 된다.

 

#include <stdio.h>
#include <algorithm> // max

using namespace std;

int a[10100], n;

int p(int i) {
    if (i==n) return a[n];
    else if (i==n-1) return a[n-1]+a[n];
    else if (i > n) return 0;
    else return max(max(p(i+1), a[i]+p(i+2)), a[i]+a[i+1]+p(i+3));
}

int main() {
    scanf("%d", &n);
    for (int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    printf("%d\n", p(1));
    return 0;
}

P 함수의 구현은 간단하다. 우리가 나눠놓았던 경우의 수를 전부 계산해서 반환해주기만 하면 된다.

 

하지만 이 구현은 큰 문제가 있다. 바로 P(i)가 P(i+1), P(i+2), P(i+3)을 호출한다는 것이다. 이 말인 즉슨 P함수가 i단계마다 3배씩 증가하며 스택을 차지하게 되고 결국 stack overflow가 발생하게 될거라는 것이다.

 

위의 P함수는 손쉽게 캐싱해두어서 다이나믹 프로그래밍을 할 수 있다.

 

#include <stdio.h>
#include <algorithm> // max
#include <cstring> // memset

using namespace std;

int a[10100], n, c[10100];

int p(int i) {
    if (c[i] != -1) return c[i];
    if (i==n) return a[n];
    else if (i==n-1) return a[n-1]+a[n];
    else if (i > n) return 0;
    else return c[i] = max(max(p(i+1), a[i]+p(i+2)), a[i]+a[i+1]+p(i+3));
}

int main() {
    memset(c, -1, sizeof(int) * 10100);
    scanf("%d", &n);
    for (int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    printf("%d\n", p(1));
    return 0;
}

별도의 C배열을 사용해서 각 P(i)의 값을 캐싱해 둠으로써 P함수가 C가 존재할때 다른 P함수들을 호출 하지 않도록 제작하였다.

 

뾰로롱

'개발 > 알고리즘 공부' 카테고리의 다른 글

자료구조 정리 C++  (0) 2018.11.08
백준 15954번 문제 풀기  (0) 2018.09.27
Comments