문제 풀이/문제 풀이(BOJ)

[Gold V] 내려가기 - 2096

풀뿌리 2024. 6. 26. 12:23

 

[문제 위치]

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

[문제 풀이]

오랜만에 금방 해결한 문제이다.

DP 문제이며 슬라이딩 윈도위의 개념이 포함되어 있다 하는데 슬라이딩 윈도우를 따로 찾아보았지만 DP 문제로만 알아도 될 것 같다.

 

문제를 푸는 방식은 간단했다.

DP를 min을 저장하는 dp와 max를 고르는 dp 두 개를 만들었다.

 

#include <iostream>
#include <vector>
#include <stack>

#define MAX 100001
#define fastio ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;

int MAP[MAX][3];
int dp_min[MAX][3];
int dp_max[MAX][3];

int main() {
    fastio;
    int N;
    cin >> N;

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < 3; j++) {
            cin >> MAP[i][j];
        }
    }
    for (int j = 0; j < 3; j++) {
        dp_max[0][j] = MAP[0][j];
        dp_min[0][j] = MAP[0][j];
    }
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < 3; j++) {
            if (j == 0) {
                dp_min[i][j] = MAP[i][j] + min(dp_min[i - 1][j], dp_min[i - 1][j + 1]);
                dp_max[i][j] = MAP[i][j] + max(dp_max[i - 1][j], dp_max[i - 1][j + 1]);
            } else if (j == 2) {
                dp_min[i][j] = MAP[i][j] + min(dp_min[i - 1][j - 1], dp_min[i - 1][j]);
                dp_max[i][j] = MAP[i][j] + max(dp_max[i - 1][j - 1], dp_max[i - 1][j]);
            } else {
                int tmp_min = min(dp_min[i - 1][j - 1], dp_min[i - 1][j]);
                dp_min[i][j] = MAP[i][j] + min(tmp_min, dp_min[i - 1][j + 1]);

                int tmp_max = max(dp_max[i - 1][j - 1], dp_max[i - 1][j]);
                dp_max[i][j] = MAP[i][j] + max(tmp_max, dp_max[i - 1][j + 1]);
            }
        }
    }

    int ans_max = max(dp_max[N - 1][0], dp_max[N - 1][1]);
    ans_max = max(ans_max, dp_max[N - 1][2]);

    int ans_min = min(dp_min[N - 1][0], dp_min[N - 1][1]);
    ans_min = min(ans_min, dp_min[N - 1][2]);
    
    cout << ans_max << " " << ans_min;
    return 0;
}

이게 가장 처음 작성한 코드이다. 구조에서 보이다 시피 처음 DP 값을 초기화 해준 다음 그 다음 위치부터 bottom-up 방식으로 해결하는 방식이다.

 

각, 자리가 세 자리 뿐이므로 해당 위치까지 도달할 수 있는 최소 혹은 최대 경로를 이전 경로에서 가져와 각 자리에 더하도록 하였다.

 

실행결과 성공적으로 동작하였지만 제출하자 메모리 초과로 실패하게 되었다.

 

이후, 메모를 감소시키기 위해 DP 배열을 다르게 배열하였다.

 

이 문제의 특징은 한 번 방문한 DP의 요소를 재방문할 일이 없다는 것이다. 모든 DP는 최소 혹은 최대 값을 구하기 위해 합산되므로 바로 이전 경로의 DP의 값들만 확인하면 되는 것이다.

 

따라서 N*3크기의 배열 전체를 만들 필요 없이 이전 배열과 현재 배열에 해당하는 1*3 배열 두 개를 만들어 사용하면 되므로 2*3의 메모리만 사용하게 된다.

 

아래는 이를 기반으로 작성한 코드이다.

#include <iostream>
#include <algorithm>

#define fastio ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;

int main() {
    fastio;
    int N;
    cin >> N;

    int MAP[3];
    int prev_min[3], prev_max[3], curr_min[3], curr_max[3];

    // 첫 번째 행 입력 및 초기화
    for (int j = 0; j < 3; j++) {
        cin >> MAP[j];
        prev_min[j] = MAP[j];
        prev_max[j] = MAP[j];
    }

    // 나머지 행 처리
    for (int i = 1; i < N; i++) {
        for (int j = 0; j < 3; j++) {
            cin >> MAP[j];
        }

        // 현재 행 계산
        curr_min[0] = MAP[0] + min(prev_min[0], prev_min[1]);
        curr_max[0] = MAP[0] + max(prev_max[0], prev_max[1]);

        int temp_min = min(prev_min[0], prev_min[1]);
        curr_min[1] = MAP[1] + min(temp_min, prev_min[2]);

        int temp_max = max(prev_max[0], prev_max[1]);
        curr_max[1] = MAP[1] + max(temp_max, prev_max[2]);

        curr_min[2] = MAP[2] + min(prev_min[1], prev_min[2]);
        curr_max[2] = MAP[2] + max(prev_max[1], prev_max[2]);

        // 현재 행을 이전 행으로 업데이트
        for (int j = 0; j < 3; j++) {
            prev_min[j] = curr_min[j];
            prev_max[j] = curr_max[j];
        }
    }

    int ans_max = max(prev_max[0], prev_max[1]);
    ans_max = max(ans_max, prev_max[2]);

    int ans_min = min(prev_min[0], prev_min[1]);
    ans_min = min(ans_min, prev_min[2]);

    cout << ans_max << " " << ans_min;
    return 0;
}