
주어진 요리의 만족도(satisfaction) 배열을 활용해서, 만족도 조리 시간(순서)의 총합이 최대가 되도록 요리 순서를 정하거나 특정 요리를 포기하는 문제.
DP 풀이로 나아가는 도출 과정은 다음과 같다.
각 요리 를 현재 시간 에 조리할지 말지 결정하는 재귀 함수 GetMaxSum(i, t)를 생각할 수 있다.
satisfaction[i] * t)를 더하고, 다음 요리는 시간 에 확인해야 함 satisfaction[i] * t + GetMaxSum(i + 1, t + 1)GetMaxSum(i + 1, t)dp[i][t] - )완전 탐색을 진행하다 보면 GetMaxSum(20, 16)처럼 동일한 요리 인덱스 와 시간 의 조합이 여러 번 중복 호출되는 문제가 발생(Overlapping Subproblems). 이를 막기 위해 계산 결과를 테이블에 저장하는 2차원 DP를 도입할 수 있다.
dp[i][t] 정의: 번째 요리부터 마지막 요리까지 고려했을 때, 현재 시간이 일 때 얻을 수 있는 최대 만족도.int cook = satisfaction[i] * t + dp[i + 1][t + 1];
int dontCook = dp[i + 1][t];
dp[i][t] = max(cook, dontCook);
dp[t] - 공간 )2차원 점화식을 다시 보면 아주 중요한 특징이 있다. dp[i][t]를 구하기 위해 필요한 값은 오직 바로 다음 행인 i + 1행의 값들(dp[i+1][t+1], dp[i+1][t])뿐이다. 즉, 이전의 모든 행을 메모리에 유지할 필요 없이 바로 직전에 계산된 하나의 행 정보만 있으면 테이블을 하나로 압축할 수 있다.
따라서 테이블을 1차원 dp[t]로 줄이고, 시간 루프를 1부터 n까지 순방향(오름차순)으로 돌리면 된다.
dp[t] = max(satisfaction[di] * t + dp[t + 1], dp[t]) 연산을 수행할 때:dp[t + 1]은 아직 이번 회차()에서 갱신되기 전의 값, 즉 이전 행()의 값. (가 커지는 방향으로 루프를 도니까 은 미래에 바뀔 값이지 지금은 이전 행의 유산임)dp[t] 역시 아직 갱신 전이므로 이전 행()의 값.dp[i][t] = max(dp[i+1][t+1]..., dp[i+1][t]) 로직이 완벽하게 성립. 최종적으로 공간 복잡도를 까지 극적으로 줄이게 됨.total)을 결과값(rst)에 한 번 더 더한다는 의미다.satisfaction[i])와 지금까지 모은 요리 만족도의 합(total)을 더했을 때 0보다 크다면(total + satisfaction[i] > 0), 이 요리를 추가하는 게 최종 결과에 무조건 이득vector<int> dp를 사용해서 각 조리 시간 에 얻을 수 있는 최대 만족도를 저장하고 갱신함.int total과 총합을 계산할 int rst 같은 스칼라 변수만 써서 메모리를 최소화함.class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
sort(satisfaction.begin(), satisfaction.end() );
int n = satisfaction.size();
vector<int> dp(n+2, 0);
for ( int di = n - 1; di >= 0; di-- )
{
for ( int t = 1; t <= n; t++)
{
int cook = satisfaction[di]*t + dp[t+1];
int dontCook = dp[t];
dp[t] = max(cook, dontCook);
}
}
return dp[1];
}
};

class Solution {
public:
int maxSatisfaction(vector<int>& satisfaction) {
sort(satisfaction.begin(), satisfaction.end() );
int n = satisfaction.size();
int rst = 0;
int total = 0;
for ( int i = n -1; i >= 0 && total + satisfaction[i] > 0; i-- )
{
total += satisfaction[i];
rst += total;
}
return rst;
}
};

단순히 "DP로 풀었다"에서 끝내는 게 아니라, 2차원 dp를 1차원 dp로 최적화한 부분이 중요했다. 특히, 왜 내부 루프를 순방향으로 돌려도 이전 행의 값(dp[t+1])이 깨지지 않고 유지되는지 그 인덱스 종속성을 정확히 이해하고 구현한 점이 중요.