NC144 不相邻最大子序列和 动态规划优化

不相邻最大子序列和

题目描述

给你一个 n ,和一个长度为 n 的数组,在不同时选位置相邻的两个数的基础上,求该序列的最大子序列和(挑选出的子序列可以为空)。

示例

  1. 示例1
    输入
    1
    3,[1,2,3]
    返回值
    1
    4
    说明
    1
    有 [], [1], [2], [3], [1,3] 4 种选取方式其中 [1,3] 选取最优,答案为 4 。 
  1. 示例2
    输入
    1
    4,[4,2,3,5]
    返回值
    1
    9
    说明
    1
    其中 [4,5] 的选取方案是在满足不同时选取相邻位置的数的情况下是最优的答案。 

解决方案

动态规划

根据题目的描述可以很快明白这是一个 01 背包问题,根据前边已经计算好的前 i 个节点的结果判断是否选择当前节点。
状态转移方程:

1
dp[i] = Math.max(dp[i-1], dp[i-2]+arr[i-1])

边界条件:

1
for (int i = 2; i <= n; i++) {}

那么就可以根据这些判断出来的条件撸代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
public long subsequence (int n, int[] array) {
long[] dp = new long[n+1]; // dp[i] 表示当前前 i 个最大和

// 初始值
dp[0] = 0;
dp[1] = array[0];

for (int i = 2; i <= n; i++) { // 边界条件
dp[i] = Math.max(dp[i-1], dp[i-2] + array[i-1]); // 当前的最大值在前一个或者前两个和现在的值相加之间选择一个最大值
}

return dp[n];
}

这样根据动态规划的理念,这个问题也就解决了。

动态规划优化

如果你还记得斐波那契数列优化版本的话,你就会发现这其中似乎有一些类似的地方,比如:计算当前第 i 个值的结果,只需要参考前两位的结果值,跟其他的都没有关系。
按照这个思路,重新理清楚思路,那么代码就来了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public long subsequence (int n, int[] array) {
long one = 0, two = array[0], temp = 0;
for (int i = 2; i <= n; i++) {
if (two < one + array[i-1]) { // 当前的最大值在 two 或者 one 和当前的值相加之间选择一个最大值,并更新对应的 two 和 one
temp = two;
two = one + array[i-1];
one = temp;
} else { // one 和 two 做向前状态转移
two = two;
one = two;
}
}
return two;
}

这个版本的优化主要在于空间复杂度的优化,以往需要一整个数组来记录,而现在只需要两个变量来代替,极大的节省了空间复杂度。


总结

算法只有你事必躬亲才会慢慢有成效,如果只靠一时的 100% check ,那么你是不会理解算法的精髓和含义。


个人备注

此博客内容均为作者学习所做笔记,侵删!
若转作其他用途,请注明来源!