[고득점Kit][DP] 도둑질 ⭐⭐⭐⭐
카테고리: Programmers
태그: Coding Test DP Algorithm
[DP] 도둑질
난이도 ⭐⭐⭐⭐
문제
이 문제를 DP 적으로 생각하는게 어려웠다. 배울게 많았던 문제이니 반복해서 보자!!
풀이
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
int solution(vector<int> money) {
int answer = 0;
int tempMax = 0;
vector<int> dp(money.size()); // 0 부터 차례대로 해당 원소까지 털었을 때의 돈의 최댓값
// 1. 첫 번째 집을 털었을 때(마지막 집은 털지 못한다.)의 전체
dp[0] = money[0];
dp[1] = dp[0];
for(int i = 2; i < money.size() - 1; i++)
dp[i] = max(dp[i - 1], dp[i - 2] + money[i]);
dp.back() = dp[dp.size() - 2];
tempMax = *max_element(dp.begin(), dp.end());
// 2. 첫 번째 집을 털지 않았을 때의 전체
dp[0] = 0;
dp[1] = money[1];
for(int i = 2; i < money.size(); i++)
dp[i] = max(dp[i - 1], dp[i - 2] + money[i]);
// 1, 2 두 경우 중 Max
answer = max(tempMax, *max_element(dp.begin(), dp.end()));
return answer;
}
동적 계획법은 피보나치 수열 구하는 것과 비슷하다는 것 기억하자~~~
A B C D 이렇게 나란히 집이 동그랗게 마주보며 있을 때, A 를 털었다면 A 의 양옆에 있는 B 와 D 의 방범 장치가 울리므로 B 와 D 는 털 수 없게 된다. A 와 이웃하지 않은 C 만 털 수 있다.
- 점화식 👉 dp[i] = max(dp[i - 1], dp[i - 2] + money[i])
dp[i]
:0
인덱스를 가진 집부터i
인덱스를 가진 집'까지' 의 모든 경우를 고려해서 누적 계산해온 돈의 최대 값- 현재 집의 운명은 다음과 같이 두가지 경우다.
- 현재 집을 털지 않는 경우 👉 0 ~ 이전집까지의 돈의 최대값을 물려받는다.
dp[i - 1]
- 이전 집은 털지 않고, 현재 집을 터는 경우 👉 0 ~ 이전이전집까지의 돈의 최대값에 현재 집의 돈을 더한다. 이전집까지의 최대값은 고려하지 않는다.
dp[i - 2] + money[i]
- 현재 집을 털지 않는 경우 👉 0 ~ 이전집까지의 돈의 최대값을 물려받는다.
- 위 두 경우 중 더 큰 돈을 가지게 되는 쪽이
i
인덱스를 가진 집까지의 집들을 터는 모든 경우들의 돈의 최대값이 될 것이다.dp[i - 1]
가 더 크면dp[i]
👉dp[i - 1]
dp[i - 2] + money[i]
가 더 크면dp[i]
👉dp[i - 2] + money[i]
- 현재 집의 운명은 다음과 같이 두가지 경우다.
- 매 집마다 현재 집을 털었을 때⭕, 안털었을때❌ 두가지 경우가 있을 텐데 이 중 더 큰 최대값을 가지는 경우를 채택하면 된다.
- 첫 번째집, 마지막 번째 집 동시에 둘 다 훔칠 수는 없기 때문에 아래와 같이 두가지 조건으로 나누어 동적계획법 계산을 진행한다. 각각 두가지 조건에서 얻은 최대값을 비교해서 최종 최대값을 선택하면 된다.
answer = max(tempMax, *max_element(dp.begin(), dp.end()));
- 첫 번째 집을 털었다면 마지막 집은 털 수 없다.
- 첫 번째 집
dp[0]
이 고려할 수 있는 경우의 수는 1 가지 뿐. 첫 번째 집을 털었을 때.money[0]
⭕ - 두 번째 집
dp[1]
이 고려할 수 있는 경우의 수는 1 가지 뿐. 두 번째 집은 못 털 때. 따라서 두 번째 집 까지의 털 수 있는 돈의 최대값은 여전히 첫번째 집에서 털었던money[0]
이다.dp[0]
⭕❌ - 세 번째 집
dp[2]
이 고려할 수 있는 경우의 수는 2 가지. 아래 두 경우 中 더 큰 최대값을 채택하면 바로 세 번째집 까지의 털 수 있는 돈의 최대값이 된다.- 세 번째 집을 털지 않는 경우 👉
dp[1]
⭕❌❌ - 두 번째 집을 털지 않고 세 번째 집을 터는 경우 👉
dp[0] + money[2]
⭕❌⭕ - 위 2 가지 경우 중 더 큰쪽을 세 번째집까지 얻을 수 있는 최대 값인
dp[2]
값으로 업뎃한다.
- 세 번째 집을 털지 않는 경우 👉
- 네 번째 집
dp[3]
이 고려할 수 있는 경우의 수는 2 가지. 아래 두 경우 中 더 큰 최대값을 채택하면 바로 네 번째집 까지의 털 수 있는 돈의 최대값이 된다.- 네 번째 집을 털지 않는 경우 👉
dp[2]
(⭕❌❌와 ⭕❌⭕ 중 큰 것중에 선택했었다.dp[2]
값에 따라 ⭕❌❌❌ 혹은 ⭕❌⭕❌ 가 될 것이다.) - 세 번째 집을 털지 않고 네 번째 집을 터는 경우 👉
dp[1] + money[3]
(⭕❌❌⭕) - 위 2 가지 경우 중 더 큰쪽을 네 번째집까지 얻을 수 있는 최대 값인
dp[3]
값으로 업뎃한다.
- 네 번째 집을 털지 않는 경우 👉
- 마지막 번째 집이 고려할 수 있는 경우의 수는 1 가지 뿐. 첫번째 집이 털렸다고 가정하고 시작했기 때문에 마지막 집은 털리면 안된다. 따라서
dp[n - 2]
이다.// 1. 첫 번째 집을 털었을 때(마지막 집은 털지 못한다.)의 전체 dp[0] = money[0]; dp[1] = dp[0]; for(int i = 2; i < money.size() - 1; i++) // for문 에서 마지막 번째 집은 제외하고 돌린다. 물론 첫번째 두번째집도! dp[i] = max(dp[i - 1], dp[i - 2] + money[i]); dp.back() = dp[dp.size() - 2]; tempMax = *max_element(dp.begin(), dp.end());
- 첫 번째 집
- 첫 번째 집을 털지 않았다면 마지막 집은 딱히 제한 없다.
- 첫 번째 집
dp[0]
이 고려할 수 있는 경우의 수는 1 가지 뿐. 첫 번째 집을 털지 않았을 때.dp[0] = 0
❌ - 두 번째 집
dp[1]
이 고려할 수 있는 경우의 수는 2 가지. ❌❌는 0 이므로 두 번째 집을 터는 ❌⭕ 경우가 최대값이 된다.dp[1] = money[1]
- 세 번째 집
dp[2]
이 고려할 수 있는 경우의 수는 2 가지.- 세 번째 집을 털지 않는 경우 👉
dp[1]
❌⭕❌ - 두 번째 집을 털지 않고 세 번째 집을 터는 경우 👉
dp[0] + money[2]
❌❌⭕- 실제로 두번째 집을 털기로 했건 안했건 그건 상관 없다. 그냥 여러 경우들 중에서 최대값을 선택하는 것 뿐이니까 !
dp[1]
가 ❌⭕ 경우의 돈을 최대값으로 선택했다고 해서 두번째 집을 실제로 털었다는게 아니라 그냥 그 경우가 두번째집까지에선 가장 큰 돈을 가질 수 있는 경로였다는 것 뿐이다. 헷갈리지 말자! 세 번째 집까지 터는 모든 경우들 중 ❌❌⭕ 가 가장 많은 돈을 털 수 있다면dp[1]
과 전혀 상관 없이 ❌❌⭕로 털었을 때의 돈이dp[2]
값으로 업뎃 되는 것이다.// 2. 첫 번째 집을 털지 않았을 때의 전체 dp[0] = 0; dp[1] = money[1]; for(int i = 2; i < money.size(); i++) dp[i] = max(dp[i - 1], dp[i - 2] + money[i]);
- 실제로 두번째 집을 털기로 했건 안했건 그건 상관 없다. 그냥 여러 경우들 중에서 최대값을 선택하는 것 뿐이니까 !
- 세 번째 집을 털지 않는 경우 👉
- 첫 번째 집
- 첫 번째 집을 털었다면 마지막 집은 털 수 없다.
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기