백준 14501번 - 퇴사1

Updated:

C++

14501번 - 퇴사

문제 출처

문제

상담을 적절히 했을 때, 백준이가 얻을 수 있는 최대 수익을 구하는 프로그램을 작성하시오.

접근 방법 및 구현

동적 계획법으로 풀어야 하는 문제이다.
동적 계획법 문제는 정말 다양한 점화식으로 접근 할 수 있는데, 제가 푼 방법을 소개해 드리겠습니다.

이 코드는 세 부분으로 나누어 지는데,

  1. 현재 상담의 금액 + 현재 상담일에서 벌어 놓은 돈 : P[i] + dp[i]
  2. 현재 상담 만큼의 기간 T[i]이 걸린 이후에 벌어 놓은 돈 : dp[i + T[i]]
  3. 현재 벌어 놓은 돈과 전날 벌어 놓은 돈을 비교 : max(dp[i], dp[i-1])

말로는 설명하기 어려우니, 표를 통해서 설명해드리겠습니다.

  1 2 3 4 5 6 7 8 9 10 11
T 5 4 3 2 1 1 2 3 4 5 x
P 50 40 30 20 10 10 20 30 40 50 x
DP 0 0 0 0 0 0 0 0 0 0 0

해당 문제의 마지막 테스트 케이스를 사용했습니다.
기존 DP값은 모두 0이며 첫번째 상담 부터 탐색합니다.

  1 2 3 4 5 6 7 8 9 10 11
T 5 4 3 2 1 1 2 3 4 5 x
P 50 40 30 20 10 10 20 30 40 50 x
DP 0 0 0 0 0 50 0 0 0 0 0

첫번째 상담이 끝난 이후입니다.
첫번째 상담 일이 끝나는 날은 i + T[i] == 6 입니다.
dp[i + T[i]] 는 당연히 0이며, P[i] + dp[i]는 50이므로 0 < 50 으로 dp[6]에 50이 저장 됩니다.

  1 2 3 4 5 6 7 8 9 10 11
T 5 4 3 2 1 1 2 3 4 5 x
P 50 40 30 20 10 10 20 30 40 50 x
DP 0 0 0 0 0 50 0 0 0 0 0

두번째 상담이 끝난 이후입니다. 앞선 결과와 달라지지 않았는데! 이유는,
두번째 상담이 끝난 날 i + T[i] == 2 + 4 == 6 이며, P[i] + dp[i] == 40 + 0 == 40
이는 첫날에 dp에 넣은 상담이 끝났을 때의 결과 50보다 작은 값이라 변하지 않았습니다.

이러한 이유로 2,3,4,5일은 전부 첫 날에 상담결과에 밀려 변동이 없습니다.
6, 7일째 상담을 진행하면

  1 2 3 4 5 6 7 8 9 10 11
T 5 4 3 2 1 1 2 3 4 5 x
P 50 40 30 20 10 10 20 30 40 50 x
DP 0 0 0 0 0 50 60 0 80 0 0

입니다. 알아서 상담을 할 시에 제일 큰 돈을 벌 수있는 방법을 추려내고 있습니다.
하지만 문제는! 8일 째 입니다.
만약 이대로 8일째에 비교를 한다면 dp[8 + 3] = P[8] + dp[8] == 30 + 0으로 30이 됩니다.
하지만 알다시피, 이전에 60만큼의 돈을 벌어 놓았으므로, 60 + 30으로 90이 되어야합니다.
따라서 여기서 필요한 코드가

dp[i] = max(dp[i], dp[i - 1]);

입니다.

이를 통해 이때까지 벌어놓은 돈 중 가장 많이 번 돈을 해당 dp에 옮겨주어, 60만큼을 벌어놨다라고 구현해줍니다.
이를 통해, 8일째 상담을 끝내면,

  1 2 3 4 5 6 7 8 9 10 11
T 5 4 3 2 1 1 2 3 4 5 x
P 50 40 30 20 10 10 20 30 40 50 x
DP 0 0 0 0 0 50 60 60 80 0 90

으로 가장 많이 번 돈은 90이 됩니다!

코드

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

#define maxN 17
int n;
int dp[maxN];
int T[maxN];	//상담을 완료하는데 걸리는 기간
int P[maxN];	//해당 상담을 끝냈을 때, 받을 수 있는 금액

int main(int argc, char* argv[]) {

	cin >> n;

	for (int i = 1; i <= n; i++) {
		cin >> T[i] >> P[i];
	}
	//첫번째 상담 부터 시작한다.
	for (int i = 1; i <= n; i++) {
		//만약 그 날 상담이 되어 있지 않을 때를 대비하여, 전날에 결과 dp를 당일에 복사한다.
		dp[i] = max(dp[i], dp[i - 1]);
		//현재 상담을 완료한 시점 < dp값이 현재 상담 금액 + 현재까지 번 돈 일시
		if (dp[i + T[i]] < P[i] + dp[i]) {
			dp[i + T[i]] = P[i] + dp[i];
		}
	}
	//가장 많이 돈을 번 상담 금액을 출력
	cout << *max_element(dp + 2, dp + n + 2);
	return 0;
}

후기 및 개선할 점

후기:

DP 문제는 고민하는 시간 대비, 코드는 정말 짧다. 이 문제도 정말 오래 고민하고, 다른 블로그를 참고하여 제대로 풀 수 있었다. DP가 제일 힘들어..