[BOJ] 2096 내려가기

2018. 9. 7. 18:04

1. 문제 링크

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


2. 해설

이 문제는 간단한 트릭을 사용하는 dp 문제이다. 평소에 dp 문제를 푸는 식으로 문제를 풀게 되면 14MB의 작은 메모리 공간에 메모리 초과라는 결과를 맞게 된다. 간단하게 메모리를 재사용하면 답을 구할 수 있다.


1. 첫 줄을 받고 int dp[3][3] 이라는 배열에 대입한다.

2. 2번째 줄부터는 2차원 dp 배열의 0번째 줄의 값들 중 가능한 최대와 최소를 구해서 1번째 줄에 대입한다.

3. 다시 1번째 줄을 0번째 줄에 대입하고 반복한다.


3. 소스코드

#include<iostream>
#include<algorithm>
#include<string>
#define fio ios_base::sync_with_stdio(0), cin.tie(0)
#define MAXNUM 100100
using namespace std;

struct temp{
    int x, n;
};

int main(){
    int n;
    temp dp[2][3];
    int map[3]={0};
    scanf("%d", &n);
    for (int i=0; i<3; i++) {
        scanf("%d", &map[i]);
        dp[0][i].x = map[i]; dp[0][i].n = map[i];
    }
    
    for (int i=1; i<n; i++) {
        for (int j=0; j<3; j++) {
            scanf("%d", &map[j]);
        }
        
        dp[1][0].n = min(dp[0][0].n, dp[0][1].n) + map[0];
        dp[1][0].x = max(dp[0][0].x, dp[0][1].x) + map[0];
        
        dp[1][1].n = min({dp[0][0].n, dp[0][1].n, dp[0][2].n}) + map[1];
        dp[1][1].x = max({dp[0][0].x, dp[0][1].x, dp[0][2].x}) + map[1];
        
        dp[1][2].n = min(dp[0][1].n, dp[0][2].n) + map[2];
        dp[1][2].x = max(dp[0][1].x, dp[0][2].x) + map[2];
        
        for (int j=0; j<3; j++) {
            dp[0][j] = dp[1][j];
        }
    }
    
    printf("%d %d\n", max({dp[0][0].x, dp[0][1].x, dp[0][2].x}), min({dp[0][0].n, dp[0][1].n, dp[0][2].n}));
    
    return 0;
}


4. 추가 언급

우리는 algorithm 헤더파일에 있는 min과 max 함수를 알고 있다. 그런데, 3개 이상의 값들 중에 가장 큰 값을 찾으려면 어떻게 해야할까? 간단한 방법으로는 max안에 max함수를 넣어서 구하는 방법이 있겠다.

max(a, max(b, c));

아래의 코드를 보면 위와 같이 구하지 않았다. 어떻게 이게 되는 것일까?

dp[1][1].n = min({dp[0][0].n, dp[0][1].n, dp[0][2].n}) + map[1];
dp[1][1].x = max({dp[0][0].x, dp[0][1].x, dp[0][2].x}) + map[1];

실제로 30줄의 max와 min 함수에 들어가서 코드를 보면 max_element와 min_element로 되어있다. 각각의 함수는 원하는 범위에서 가장 큰 값과 가장 작은 포인터(이터레이터)를 얻어 오는 함수이다. max코드는 아래와 같이 구현되어 있다. 물론 min도 이렇게 되어있다.

template<class _Tp>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_Tp
max(initializer_list<_Tp> __t)
{
    return *_VSTD::max_element(__t.begin(), __t.end(), __less<_Tp>());
}

template <class _ForwardIterator, class _Compare>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
_ForwardIterator
max_element(_ForwardIterator __first, _ForwardIterator __last, _Compare __comp)
{
    if (__first != __last)
    {
        _ForwardIterator __i = __first;
        while (++__i != __last)
            if (__comp(*__first, *__i))
                __first = __i;
    }
    return __first;
}

3번째 줄을 보면 중괄호로 묶었을 때, 알아서 list 객체를 생성해서 인자로 주는 방식임을 알 수 있다.


그렇다면 그냥 중괄호로 묶는 것과 max함수 안에 max를 넣는 방법 중 어느 것이 더 빠를까?


첫번째로 객체를 만드는 max값을 구하는 방법으로 측정한 시간을 보자.

#include<iostream>
#include<ctime>
using namespace std;

int main(){
    int arr[4] = {0, 1, 2, 3};
    int t = 100000000;
    double elapsedTime = clock();
    while (t--) {
        int wantValue = -1;
        wantValue = max({arr[0], arr[1], arr[2], arr[3]});
    }
    
    printf("총 걸린 시간은 %lf초 입니다.\n", (clock() - elapsedTime)/CLOCKS_PER_SEC);
    
    return 0;
}


두번째로는 그냥 max안에 max를 넣어 구하는 방법으로 측정한 시간을 보자.

#include<iostream>
#include<ctime>
using namespace std;

int main(){
    int arr[4] = {0, 1, 2, 3};
    int t = 100000000;
    double elapsedTime = clock();
    while (t--) {
        int wantValue = -1;
        wantValue = max(arr[0], max(arr[1], max(arr[2], arr[3])));
    }
    
    printf("총 걸린 시간은 %lf초 입니다.\n", (clock() - elapsedTime)/CLOCKS_PER_SEC);
    
    return 0;
}


이 결과를 보면 알 듯이 실제로 max 함수 안에 max를 넣으면 조금 더 빠른 것을 알 수 있다. 차이라고 하면 객체를 생성하는데에 걸리는 시간일 테니, 코딩 코스트는 줄어들지만 실제로 퍼포먼스적인 측면에서는 더 좋지 않음을 알 수 있다. 하지만 문제를 푸는 것에 있어서는 유의미한 결과를 가져오지는 않을 것으로 보이므로 코딩코스트가 줄어든다는 측면에서는 좋은 방법인듯 하다.


'프로그래밍 > 문제풀이(boj)' 카테고리의 다른 글

[BOJ] 10845 큐  (0) 2018.09.11
[BOJ] 1697 숨바꼭질  (0) 2018.09.11
[BOJ] 1526 가장 큰 금민수  (0) 2018.09.07
[BOJ] 1525 퍼즐  (0) 2018.09.06
[BOJ] 2225 합분해  (0) 2018.09.06

BELATED ARTICLES

more