CS/Algorithm

[프로그래머스][dp] 땅따먹기

아란정 2024. 6. 26. 23:56

프로그래머스 알고리즘 강좌가 dp 이해에 도움이 됐다는 글을 보고 '땅따먹기'를 시도했다. 직접 짜보고 dp 코드와 비교하니까 dp의 느낌이 확 와닿는다. dp는 모든 경우의 수를 확인하는데, 각 요소가 있고, 없고에 따라 경우를 만든다. 

문제와 함께 보자

 

문제

ㅎ. 하지만 나는 어제 dp를 정리해 놓고도 전혀 떠오르지 않아서 풀리는 대로 풀었다. (이해가 덜 됐다ㅜ)

내 풀이 방법

1. 현재 index(j)를 이전 max의 index와 비교해 같지 않은 경우에만 들어간다. 
  - if ( i == 0 ) 인 예외를 주는 데 시간이 오래 걸렸다. 다시 생각해 보니 첫 열은 index가 뭐든 관계없으니까 preindex 처음 선언할 때 preindex = -1; 했으면 됐겠다. 

2. index가 같지 않은 경우에만 max의 값을 업데이트할 기회를 준다.

3. max 값은 answer에 바로 저장하고 검사용 preindex를 업데이트한다.

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

int main() {
    vector<vector<int>> land = {
        {1, 2, 3, 5},
        {5, 6, 7, 8},
        {4, 3, 2, 1}
    };

    int answer = 0, preindex, index, max;

    for (int i = 0; i < land.size(); i++) {
        max = 0, index = 0, preindex = 0;
        for (int j = 0; j < 4; j++) {
            if (i==0 or j != preindex) {
                if (max < land[i][j]) {
                    max = land[i][j];
                    index = j;
                }
            }
        }
        answer += max;
        preindex = index;
    }

    return answer;

}

 

테스트 코드만 통과했다. 게다가 비효율적이다. 만약에 land [ 3 ][ j ] 값을 이용한다 치면 이미 했던 계산을 또 해야 하기 때문이다. 

해설은 dp를 이용했다. 이제 약간 감이 잡혔다.  dp의 memoization은 갔던 모든 경로를 이전에 구한 값을 이용해 만드는 배열이다. 

 

해설 코드

#include<vector>
using namespace std;

int dp[100001][4] = {0};
int solution(vector <vector<int> > land) {
    int r = land.size();
    for (int i = 0; i < 4; ++i) {
        dp[0][i] = land[0][i];
    }

    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < 4; ++j) {
            for(int k = 0; k < 4; ++k) {
                if (j != k) {
                    dp[i][j] = max(dp[i][j], land[i][j] + dp[i-1][k]);
                    //dp에 넣을 값은 현재 land를 넣지 않는 경우와 넣는 경우(0, 1)를 말한다.
                }
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < 4; ++i) {
        ans = max(ans, dp[r-1][i]);
    }
    return ans;
}

 

위에서부터 아래로 내려가는 방법 ↓ 이랑 아래에서 위로 올라가는 코드 ↑ 가 있고, 아래에서 위로 올라가는 코드도 익숙해져야한다고 했다. 각각 하향식(Memoization)와 상향식 풀이(Tabulation) 를 의미한다. 상향식 풀이의 예는 피보나치에서 f[0] = 0, f[1] = 1 이렇게 요소의 값을 넣어놓고 이를 조합해 f[n]을 구하는 방식을 의미한다. 

 

<어려워>
오늘 계절 미적분학 교수님께서 대학 공부는 원칙대로 차근차근 이해하는 게 아니라 일단 한 번 훑고 나중에 다시 보고 이해하는 거라고 하셨다. 아몰랑 나는 그렇게 해야겠다 (눈귀막귀)

 

< DP 배낭의 응용이므로 배낭을 이해하기 좋은 영상을 첨부했다. >

https://youtu.be/rhda6lR5kyQ?si=LmfgjoV-pNC0u3Sf