LeetCode初级算法题回顾(四)动态规划

本回顾是对LeetCode 探索栏目中的“初级算法题”进行总结,归纳该栏目中做题的心得和体会。(注:不是leetcode中的所有简单难度的算法题)

该探索项目分为九个子栏目:数组、字符串、链表、树、排序和搜索、动态规划、设计问题、数学及其他。

 

动态规划


首先推荐一篇博文——算法-动态规划 Dynamic Programming--从菜鸟到老鸟,From HankingHu。有关于动态规划的方法的简单梳理,赋算法题例子,很好理解。 关于最后一个讲例,小朋友过河感觉是个很经典的动态规划问题:可以好好理解一下。

 opt[i] = min{opt[i-1] + a[1] + a[i] , opt[i-2] + a[1] + a[i] + 2*a[2] }
  • opt[i-1] + a[1] + a[i]                                  //河对岸只有一人,由a[i]走去对岸送手电筒,同a[i]一起回来 opt[i-1] + a[1] + a[i]
  • opt[i-2] + a[1] + a[i] + 2*a[2]                    //河对岸有两人 a[i],a[i-1]。 由a[2]先送手电筒,a[i]带着a[i-1]一起回来,再有a[1]去接a[2],跟a[2]一起回来。 opt[i-2] + a[1] + a[i] +2*a[2]
  • 其实比的是 a[1]+a[i-1] 与 a[2]+a[2]谁更大

动态规划算法的两种形式:(博文以求斐波拉契数列为例,与普通递归方法做了比较)

  1. 自顶向上的备忘录法
  2. 自底向上法(better)

动态规划LeetCode初级算法题一览

1)爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定 n 是一个正整数。

示例 1:输入: 2 输出: 2 解释: 有两种方法可以爬到楼顶。 1. 1 阶 + 1 阶 2. 2 阶

示例 2:输入: 3 输出: 3 解释: 有三种方法可以爬到楼顶。 1. 1 阶 + 1 阶 + 1 阶 2. 1 阶 + 2 阶 3. 2 阶 + 1 阶

根据假设n=1,2,3,4....可以发现本质是一个求斐波拉契数列的算法题

class Solution {
public:
    int climbStairs(int n) {
        //最好用自底向上的解法
        if(n<=0) return 1;

        int n_1=0;
        int n_2=1;
        int num;

        for ( int i=0; i

4)打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例 1:   输入: [1,2,3,1]    输出: 4

解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。   偷窃到的最高金额 = 1 + 3 = 4 。

思路上该题与上一题别无二致

class Solution {
public:
    int rob(vector& nums) {//自底向上
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return nums[0];
    
        int robs1 = nums[0];
        int robs2 = nums[1];
        robs2 = max(robs1,robs2); 
        if (nums.size() == 2) return robs2;

        int rob;
        for (int i=2;i& nums) {//备忘录
        if ( nums.size() == 0) return 0;
         
        vector money(nums.size()+1);
        money[1] = nums[0];
        
        for( int i=2; i<=nums.size();i++)
            money[i] = max (money[i-2]+nums[i-1], money[i-1]);
      
        return money[nums.size()];
    }
};
 

2)买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。

注意你不能在买入股票前卖出股票。

示例 1:  输入: [7,1,5,3,6,4] 输出: 5

解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

示例 2:  输入: [7,6,4,3,1] 输出: 0

解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

 //我们只关心 最小值是否发生了变化 当前值与最小值的差值profit又是否发生了增长(即最大 最小 值是否变化)
class Solution {
public:
    int maxProfit(vector& prices) {
        if (prices.size() <=1 )  return 0;
        
        int min = prices[0];
        int profit = 0;
      
        for (int i=1; i

 


3)最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6

解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

进阶:如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

方法一:动态规划

核心idea:

n个数的最大子序和 = max( 以第n个数结尾的最大子序和 , n-1个数的最大子序和)

以第n个数结尾的最大子序和 = max (第n个数,以第n-1个数结尾的最大子序和+第n个数)

所以:

n个数的最大子序和 = max(第n个数,以第n-1个数结尾的最大子序和+第n个数 , n-1个数的最大子序和)

//解决核心
//(1) 长度为i+1的最大子列和 = max(以第i+1个数结尾的最大子列和, 不以第i+1个数结尾的最大子列和)。* 不以第i+1个数结尾的子列=长度为i的最大子列和
//(2) 以第i+1个数结尾的最大子列和 = max (a[i+1], 以第i个数结尾的最大子列和 + a[i+1])。 
// 即   长度为i+1的最大子列和 = max(  max( a[i+1],以第i个数结尾的最大子列和 + a[i+1] ) ,  长度为i的最大子列和 )

class Solution {
public:
    int maxSubArray(vector& nums) {//备忘录
        if (nums.size()==0) return 0;
        vector maxsum(nums.size()+1);//长度为i的最大子列和
        vector maxend(nums.size()+1);//以第i个数结尾的最大子列和
        maxsum[1]=nums[0];
        maxend[1]=nums[0];
        
        for( int i=2; i<=nums.size();i++){
            maxend[i] = max ( nums[i-1], maxend[i-1]+nums[i-1]);
            maxsum[i] = max ( maxend[i], maxsum[i-1]);
        }
        return maxsum[nums.size()];
    }

    int maxSubArray2(vector& nums) {//自底向上
        if( nums.size() == 0 ) return 0;
        if( nums.size() == 1 ) return nums[0];
        int maxsub;
        int maxend;
        int maxsub1 = nums[0];
        int maxend1 = nums[0];
        for ( int i=1;i

方法二:分治法 

END

你可能感兴趣的:(Leetcode,代码实现与解析)