Computer Science
탄탄한 기반 실력을 위한
전공과 이론 지식 모음
Today I Learned!
배웠으면 기록을 해야지
TIL 사진
Flutter 사진
Flutter로 모바일까지
거꾸로캠퍼스 코딩랩 Flutter 앱개발 강사
스파르타코딩클럽 즉문즉답 튜터
카카오테크캠퍼스 3기 학습코치
프로필 사진
박성민
임베디드 세계에
발을 들인 박치기 공룡
임베디드 사진
EMBEDDED SYSTEM
임베디드 SW와 HW, 이론부터 실전까지
ALGORITHM
알고리즘 해결 전략 기록
🎓
중앙대학교 소프트웨어학부
텔레칩스 차량용 임베디드 스쿨 3기
애플 개발자 아카데미 1기
깃허브 사진
GitHub
프로젝트 모아보기
Instagram
인스타그램 사진

Develop/알고리즘

[백준] 2156 - 포도주 시식

sm_amoled 2021. 8. 10. 12:29

문제링크

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

문제

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

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

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

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

입력

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

출력

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

조건

  • 시간 제한 : 2s
  • 메모리 제한 : 128MB

해설

연속으로 3개의 잔을 마실 수 없다는 조건 때문에 계단 문제와 비슷하다고 생각될 수 있지만, 중간에 1칸/2칸씩만 뛰어넘을 수 있다는 조건이 없어서 3칸 씩 뛰어넘는 경우도 가능하다. 이를 고려해주어야 한다. 나머지는 계단 문제와 동일하다.

풀이

vinum 벡터에 각 포도주의 양을, vinumMax 벡터에 그 잔까지에서 선택할 수 있는 방법 중 가장 많은 양을 저장한다. 가장 마지막 잔을 무조건 마신다고 가정하자. vinum[n]을 마신다면 선택할 수 있는 이전 잔은 vinum[n-1]vinum[n-2]이다. 여기에서 최댓값을 구하기 위해서 더 큰 값을 선택하게 된다. (n-3을 선택하는 것은 n-3 → n-1 → n 또는 n-3 → n-2 → n 으로 더 많은 잔을 선택할 수 있는 경우가 있기 때문에, 생략한다.) 그렇다면, vinumMax[n] 값에 대한 식은 아래와 같이 나타낼 수 있다. n-2를 선택하는 경우에는 vinum[n-2]까지의 최댓값 + vinum[n]이 되고, n-1을 선택하는 경우에는 연속으로 마시는 경우를 배제하기 위해 vinum[n-3]까지의 최댓값 + vinum[n-1] + vinum[n]이다.

vinumMax[n] = max(vinumMax[n-2] + vinum[n], 
                  vinumMax[n-3] + vinum[n-1] + vinum[n]);

여기에서 2칸을 뛰어넘는 경우를 따져주기 위해서, vinumMax[i]vinumMax[i-1]의 값을 비교하여 더 큰 값을 vinumMax[i]에 대입해준다. 굳이 1칸 또는 2칸만 뛰어넘을 필요가 없으니 제일 큰 값에서 뛰어넘는 것이다.

vinumMax[i] = max(vinumMax[i], vinumMax[i-1]);

반복문이 끝나고 나면 vinumMax[n]에 우리가 찾고자 하는 최댓값이 들어있다.


코멘트

이건 어려워서 답을 참고했다. 코드를 복잡하게 짜다가, 깔끔하지 않아서 져버렸어. vinMax를 비교해서 대입해주는걸 넣었으면 나도 성공했을 것 같은데... 어려웠다. 그리고 계단 문제처럼 max[i-2] + arr[i] , max[i-3] + arr[i-1] + arr[i] 이런 꼴로 생각해내는게 좀 어려운 것 같아. 연습을 많이 하면서 익숙해져야겠다.


코드

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main () {
    int n;
    cin >> n;
    vector<int> vinum(n+1);
    vector<int> vinumMax(n+1, 0);
    
    cin >> vinum[1] >> vinum[2];
    vinumMax[1] = vinum[1];
    vinumMax[2] = vinum[1] + vinum[2];
    
    for(int i = 3; i <= n; i++) {
        cin >> vinum[i];
        
        vinumMax[i] = max(vinumMax[i-2] + vinum[i], vinumMax[i-3]+vinum[i-1]+vinum[i]);
        vinumMax[i] = max(vinumMax[i], vinumMax[i-1]);
    }
    
    cout << vinumMax[n];
    return 0;
}
320x100

'Develop > 알고리즘' 카테고리의 다른 글

[백준] 10870 - 피보나치 수 5  (0) 2021.08.10
[백준] 10989 - 수 정렬하기 3  (0) 2021.08.10
[백준] 10825 - 국영수  (0) 2021.08.10
[백준] 1912 - 연속합  (0) 2021.08.10
[백준] 2752 - 세수정렬  (0) 2021.08.10