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<n;i++){
            num = n_1+n_2;
            n_1 = n_2; 
            n_2 = num;
        }
        return num;
        
    }
};

4)打家劫舍

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

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

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

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

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

class Solution {
public:
    int rob(vector<int>& 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.size();i++){
            rob = max(robs2,robs1+nums[i]);
            robs1 = robs2;
            robs2 = rob;
        }
        return rob;
    }
    int rob2(vector<int>& nums) {//备忘录
        if ( nums.size() == 0) return 0;
         
        vector<int> 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<int>& prices) {
        if (prices.size() <=1 )  return 0;
        
        int min = prices[0];
        int profit = 0;
      
        for (int i=1; i<prices.size(); i++){
            if(prices[i]<min)  min = prices[i];
            profit = max ((prices[i]-min),profit);
         
        }
        return profit;
    }
};

 


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<int>& nums) {//备忘录
        if (nums.size()==0) return 0;
        vector<int> maxsum(nums.size()+1);//长度为i的最大子列和
        vector<int> 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<int>& 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<nums.size(); i++){
            maxend = max( maxend1+nums[i],nums[i]);
            maxsub = max( maxend, maxsub1);
            maxend1 = maxend;
            maxsub1 = maxsub;
        }
        
        return maxsub;
    }

}; 

方法二:分治法 

END

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值