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



이 문제는 5개월 전에 풀 때, 3개월 전에 풀 때, 그리고 지금 다시 풀 때 항상 틀린 문제다.
정답률이 34%에 이르는데 정답률이 비슷한 다른 문제보다 체감 난이가 더욱 높게 느껴졌다.
막상 다 풀고나면 그닥 어려운 느낌은 아닌데 중간에 생각하지 못한 까다로운 부분이 있어서 항상 풀때마다 실수하고 틀렸던 것 같다. 

물론 알고리즘을 잘하는 사람들에게는 굉장히 쉬운 문제로 보여서....평소에 알고리즘 공부를 더 열심히 해야겠다고 생각나게 하는 문제이다...


본론으로 들어가서 

우선 n값이 1부터 10000사이의 값을 보고 10000을 제곱하게 되버리면 바로 시간초과가 나게 된다.

dp로 접근할 때 점화식을 어떻게 세우냐에 따라서 여러가지 답이 나올 수 있다. 

그리고 처음에 어떻게 점화식을 세워서 접근할 것인지가 제일 중요한 문제이다. 


n번째 포도주를 선택해서 마시는 경우 직전에 마신 포도주가 n-1번째 인 경우, n-2번째인 경우로 문제에 접근할 수 있다. 

또는 1번 연속 마시는 경우, 2번 연속 마시는 경우와 같이 점화식을 세울 수도있다. 

사실 두 접근법은 크게 차이가 나지는 않지만 실제로 구현할 때 배열 원소를 조금 다르게 해줘야 하는 차이가 있다. 

중요한건 점화식을 세운 다음에 이와 관련해서 접근하는 것이고 나는 두 번째 방식으로 문제에 접근했다.



우선 점화식은 다음과 같다.
dp[1][n] = n번째 포도주를 1번 연속 마셨을 때의 최대 값 (n-1번째는 마시지 않음)
dp[2][n] = n번째 포도주를 2번 연속 마셨을 때의 최대 값 (n-1번째 마시고 바로 n번째를 마시는 경우)


dp[2][n]의 경우에는 2번 연속 마신 경우이기 때문에 n번째 포도주의 값에다가 바로 dp[1][n-1]을 더해주면 된다. 


그런데 문제는 dp[1][n]의 점화식이다.
dp[1][n]=max(dp[1][n-2],dp[2][n-2])+n번째 포도주
위와 같이 점화식을 세우고 고민하다가 바로 n-3번째를 고려하지 않았을 경우에 반례를 생각하게 됐다.


5개의 포도주가 있다고 가정하고 그 값을 각각 "20", "10", "9", "1", "2" 라고 생각해보자


여기서 5번째에서 위의 dp 점화식을 적용하면 3번째의 최대값을 고려해야 하고 3번째에서 나올 수 있는 최대값은 20과 9를 더한 29일 것이다.
그러나 2번째 원소까지 고려한다면 20과 10을 더한 30이 최대값이고 결론적으로는 n-2번째보다 n-3번째의 포도주의 최대값이 더 큰 값이 될것이다.
만약에 n-4번째 까지 가게 된다면, 이미 n-1번째와 n-2번째에서 각각의 점화식에 맞게 최대값을 구한 것이기 때문에 n번째에서는 n-3번째까지 고려하면 된다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
 
int main(){
    int n,ans=0;
    cin>>n;
    
    vector<int> vec(n+1,0);
    for(int i=1;i<=n;i++)
        scanf("%d",&vec[i]);
    
    vector<vector<int>> dp(3,vector<int> (n+1,0));
    
    dp[1][1]=dp[2][1]=vec[1];
    dp[1][2]=vec[2], dp[2][2]=vec[1]+vec[2];
    ans=(n==1)? vec[1]:vec[1]+vec[2]; //n이 1이거나 2일 경우
    
    for(int i=3;i<=n;i++){
        dp[1][i]=max(max(dp[1][i-3],dp[2][i-3]),max(dp[1][i-2],dp[2][i-2]))+vec[i];
        dp[2][i]=dp[1][i-1]+vec[i];
        
        ans=max(ans,max(dp[1][i],dp[2][i]));
    }
    cout<<ans<<endl;
}
cs


'알고리즘(BOJ) > DP' 카테고리의 다른 글

백준 1010번 - 다리놓기  (0) 2018.01.25
백준 1912번 - 연속합  (0) 2018.01.25
백준 11726번 - 2xN 타일링  (0) 2018.01.25
백준 2193번 - 이친수  (0) 2018.01.25
백준 9095번 - 1,2,3 더하기  (0) 2018.01.25

+ Recent posts