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

输入:3,[1,2,3]
返回值:4
说明:有[],[1],[2],[3],[1,3] 4种选取方式其中[1,3]选取最优,答案为4

这道题本质上是“打家劫舍”问题,可以用动态规划解决,我们换个说法解释这道题:“当一个盗贼打算偷抢房屋的钱财时,两间相邻的房屋在同一个晚上被抢时,系统会自动报警,在不触碰到报警设备时,求他能抢到的最高金额。”
解决动态规划问题首先要找到状态选择,这里的状态就是你面前房子的索引,
选择有两个:
1.不抢这间房子,到下一间做选择
2.抢这间房子,那么下一间就不能抢,只能走到下下一间做选择

图片说明
方法一:自顶向下(递归)
图片说明
从上面这张递归的图片上看,需要计算,而会重复计算,为了避免重复计算,减小复杂度,我们可以定义一个记忆数组,将其初始化为全部是-1,每计算出一次结果,将其记录到数组中,如果的值不是-1,说明已经被计算过,则直接返回结果,否则继续进入下一层递归计算

C++版本

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 计算
     * @param n int整型 数组的长度
     * @param array int整型vector 长度为n的数组
     * @return long长整型
     */

    long long subsequence(int n, vector<int>& array) {
        // write code here
        vector<long long>memory(n);
        //初始化memory数组为-1
        for(int i=0;i<n;i++){
            memory[i]=-1;
        }
        return dp(array,0,memory);
    }
    long long dp(vector<int>&array,int start,vector<long long>&memory){
        //递归终止条件
        if(start>=array.size())return(long)0;
        //避免重复计算
        if(memory[start]!=-1)return memory[start];
        //不抢这间房子到下一间房子做选择与抢这间房子,到下下一间房子做选择之间择优
        long long res= max(dp(array,start+1,memory),(long long)array[start]+dp(array,start+2,memory));
        //对已经计算过的进行标记
        memory[start]=res;
        return res;
    }
};

复杂度:

  • 时间复杂度:,初始化记忆数组的时间复杂度为,递归的时间复杂度在于每次递归的时间复杂度和递归深度,每次递归的操作复杂度为常数级,递归深度为,因此时间复杂度为
  • 空间复杂度:记忆数组的大小为,递归深度为 ,因此空间复杂度为

方法二:自底向上
递归算法的复杂度过高,当数组太大时递归深度过深会导致运行超时,因此我们可以考虑用dp数组来记录状态,要求得,我们需要从后往前递推数组,因此我们从后往前遍历数组
图片说明
代码如下:

import java.util.*;
public class Solution {
    /**
 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 计算
 @param n int整型 数组的长度
 @param array int整型一维数组 长度为n的数组
 @return long长整型
      */
     public long subsequence (int n, int[] array) {
         // write code here
         //dp[i]代表从第i间房子开始抢,能抢劫到的最多的钱
         long[]dp=new long[n+2];
         for(int i=n-1;i>=0;i--){
             dp[i]=Math.max(dp[i+1],array[i]+dp[i+2]);
         }
         return dp[0];
     }
 }
  • 复杂度:
  • 时间复杂度:需要遍历一遍数组,数组长度为,则时间复杂度为
  • 空间复杂度:额外辅助空间数组的大小为,空间复杂度为

方法三:状态压缩后的动态规划
我们发现,状态转移方程只与和dp有关,因此为了提升空间效率,可以采用迭代的方法更新

import java.util.*;
public class Solution {
    /**
 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 计算
 @param n int整型 数组的长度
 @param array int整型一维数组 长度为n的数组
 @return long长整型
      */
     public long subsequence (int n, int[] array) {
         // write code here
         //分别记录dp[i+1],dp[i+2]
         int dp_i=0,dp_i1=0,dp_i2=0 ;
         for(int i=n-1;i>=0;i--){
             dp_i=Math.max(dp_i1,array[i]+dp_i2);
             dp_i2=dp_i1;
             dp_i1=dp_i;
       }
         return dp_i;
     }
 }

复杂度:

  • 时间复杂度:需要遍历一遍数组,数组长度为,则时间复杂度为

  • 空间复杂度:额外辅助空间为常数级,空间复杂度为