[BaekJoon]1823번: 수확
동적계획법 DynamicProgramming
문제
1 × N 크기의 긴 밭에 벼가 심어져 있다. 준희는 이 벼를 수확 하려고 한다. 그런데 가운데 있는 벼를 수확을 하려면 끝에서 가운데까지 헤집고 들어가야 하므로 양 끝에 있는 벼만 수확을 할 수 있다. 처음에는 첫 번째와 N번째 벼를 수확을 할 수 있을 것이며 만약에 첫 번째 벼를 수확을 하였다면 두 번째 벼와 N번째 벼를 수확을 할 수 있다.
수확을 하였을 때 얻을 수 있는 이익은 다음과 같다. 만약에 그 벼의 가치가 v(i)라고 하고 그 벼를 k번째로 수확을 한다고 하면 v(i) × k 만큼의 이익을 얻게 된다.
만약에 벼의 가치가 차례로 1 3 1 5 2 라고 하고 첫 번째, 다섯 번째, 두 번째, 세 번째, 네 번째의 순서대로 수확을 한다고 하면 1×1 + 2×2 + 3×3 + 4×1 + 5×5 = 43 이 되어 43 만큼의 이익을 얻게 된다. 그리고 이 값이 저 벼로 얻을 수 있는 최대 이익이 된다.
우리가 구해야 할 값은 다음과 같다. 벼의 개수 N과 벼의 가치가 주어져 있을 때, 얻을 수 있는 최대 이익을 구하는 프로그램을 작성하시오.
입력
첫째 줄에 벼의 개수 N(1 ≤ N ≤ 2,000)이 주어지고 두 번째 줄부터 N+1번쨰 줄까지 벼의 가치 v(i) (1 ≤ v(i) ≤ 1,000) 가 주어진다.
출력
첫째 줄에 얻을 수 있는 최대 이익을 출력한다.
예제 입력 1
5
1
3
1
5
2
예제 출력 1
43
💻코드
/*
* [백준 1823] 수확
* 성냥개비는 숫자를 나타내기에 아주 이상적인 도구이다. .
* 성냥개비의 개수가 주어졌을 때, 성냥개비를 모두 사용해서 만들 수 있는 가장 작은 수와 큰 수를 찾는 프로그램을 작성하시오.
* [알고리즘]
* 다이나믹 프로그래밍
*
* 그리디처럼 지역적으로 이익이 최대라고 해서 결과값이 최적해(최댓값)이 되지 않는다. -> 재귀로 끝까지 계산해봐야한다.
* 이미 저장된 값은 바뀌지 않으므로 한번 정해진 값은 고정적이다 -> DP
* DP이므로 결과값을 저장해놓고 비교한다.
* 왼쪽과 오른쪽으로 수확하게 되므로 dp[left][right]에 left까지 right까지 수확한 최대 이익을 저장한다.
* 저장할때마다 끝까지 가야하므로, 시간복잡도는 O(N^2) 최대 2000^2
*
* 점화식 dp[left][right]
= max(왼쪽 수확했을 때 최종 이익, 오른쪽 수확했을 때 최종 이익)
= max(dp(left+1, right, count+1) + 방금 수확한 이익, dp(left, right -1, count+1) + 방금 수확한 이익)
*
*/
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int res[2001][2001]{ 0, };
int w[2001]{ 0, };
int dp(int left, int right, int count) {
if (left > right) return 0;
if (res[left][right])return res[left][right];
int ret = max(dp(left + 1, right, count + 1) + w[left] * count, dp(left, right - 1, count + 1) + w[right] * count);
res[left][right] = ret;
return ret;
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(NULL);
cout.tie(NULL);
int N; cin >> N;
for (int i = 1; i <= N; i++) cin >> w[i];
cout << dp(1, N, 1);
return 0;
}
// 처음엔 vector<int> w로 가치를 저장하는 벡터를 지역변수로 선언하여,
// dp함수에 벡터까지 같이 넘겨주었는데 시간초과가 났다...
// 이게 이렇게 시간이 많이 걸릴 일인가..? 배열은 매개변수로 넘겨줄때 배열을 복사하는 것이 아니라 포인터 그니까 그 주소를 넘겨서 참조하는 것으로 알고 있는데