问题描述
给出一个序列 arr ,在 arr 中选择若干个互补相邻的元素组成子序列,计算子序列和的最大值
方法1 —— 递归实现
- 考虑使用 递归,定义rect_func(i)为子序列所有元素均在元素arr[i]之前时,子序列的最大值
- 递归终止条件为i == 0 或 i == 1
rect_func(i) = max(rect_func(i-2) + arr[i], rect_func(i-1))
,其中rect_func(i-2) + arr[i]
代表选择了元素arr[i],rect_func(i-1)
代表未选择元素arr[i](因此可以选择arr[i-1],问题变为rect_func(i-1))
程序设计
int rect_opt(const int a[], int i){
if (i == 0) return a[0];
if (i == 1) return std::max(a[0], a[1]);
int A = rect_opt(a, i-2) + a[i];
int B = rect_opt(a, i-1);
return std::max(A, B);
}
其中,传入的a[]为原始数组,i表示以在元素arr[i]之前选择子序列。
方法1 —— 动态规划
由于当初始序列很长时, 递归调用要计算很多重叠子问题,占用太多内存,计算速度慢,考虑用数组来存储每个子问题的结果 —— 动态规划实现。
- 状态转移方程为:dp(i) = max(dp(i-2) + arr[i], dp(i-1)),dp(i)表示子序列各个元素均在arr[i]之前
- 先初始化 dp[0] 和 dp[1]
- 循环计算dp的所有值
程序设计
int dp_opt(const int a[], int n){
if (n == 1) return a[0];
else if (n == 2) return max(a[0], a[1]);
int dp[n];
dp[0] = a[0];
dp[1] = max(a[0], dp[1]);
for (int i=2; i<n; i++){
dp[i] = max(dp[i-1], dp[i-2] + a[i]);
}
return dp[n-1];
}
结果
int main()
{
int n = 7;
int a[n] = {1, 2, 4, 1, 7, 8, 3};
cout << rect_opt(a, 6) << endl;
cout << dp_opt(a, n);
return 0;
}
此时输出结果为:15,选择的序列为 {1, 4, 7, 3},正确!
问题:如何输出被选择的元素?
解答:获取动态规划的整个数组(所有记录),从后往前,用当前opt[i]与opt[i-2] + arr[i]作比较,若相等则说明 arr[i] 被选了。
参考内容: 动态规划