20天速通LeetCodeday17:一维动态规划

前言

今日练习目的:从一维动态规划入手,理解"子问题是更小规模的父问题与子问题最优解"两大核心特征,建立"用历史结果推导当前解"的思维。

重要思维突破:理解动态规划本质是用数组记录子问题答案,吧自顶向下的递归,转化为自底向上的递推,但要注意,并非所有递归都可以转为动态规划。

509:斐波那契数列

题目要求:给定一个整数n,求第n个斐波那契数

核心思路(一维动态规划)

用数组dp[i]存储第i个斐波那契数

遍历从2到n

dp[i]=dp[i-1]+dp[i-2]

代码实现

java 复制代码
class Solution {
    public int fib(int n) {
        if(n==0) return 0;
        if(n==1) return 1;

        int[] dp=new int[n+1];//dp[i]表示第i个斐波那契数
        dp[0]=0;
        dp[1]=1;

        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];

    }
}

总结

作为入门的一维动态规划。核心需要理解:一维DP就是沿着线性状态从前往后累积计算,每个状态只依赖前两个状态

53:最大子数组和

题目要求:给定一个整数数组nums,找出一个拦蓄子数组,使得他们的和最大

要求:返回这个最大和

核心思路

假设dp[i]表示以nums[i]结尾的最大子数组和:dp[i] = max(dp[i-1] + nums[i], nums[i])

如果把前面的子数组加上nums[i]比nums[i]小-就从nums[i]重新开始

每一步更新全局最大值maxSum

代码实现

java 复制代码
int n=nums.length;
int[] dp=new int[n]
dp[0]=nums[0];
int maxSum=dp[0];

for(int i=1;i<n;i++){
dp[i]=Math.max(dp[i-1]+nums[i];nums[i]);//状态转移
maxSum=Math.max(maxSum,dp[i]);
}
return maxSum;

总结

本题重点只有一个,就是理解dp[i]的递增实现

理解dp[i] = max(dp[i-1] + nums[i], nums[i])就能掌握本题

想要讲的重点是关于递归和一维动态规划的区别

递归的特点是:

自己调用自己,没有记录已经计算过的结果

DP的特点是:

讲递归的重复计算结果记住

用一个数组dp[i]存储每个状态的结果

自底向上(从小状态开始计算到大状态)

198:打家劫舍

题目要求:

有一个整数数组nums,表示每个房子里的钱,你不能抢相邻的房子,求你能抢到的最大金额。

核心思路

对于每个房子i,有两个选择:

  1. 抢当前房子-就不能抢上一个房子,只能加上dp[i-2];
  2. 不抢当前房子-最大金额=dp[i-1]
    状态转移方程:dp[i] = max(dp[i-1], dp[i-2] + nums[i])

代码实现

java 复制代码
if(nums.length==0||nums==null) return 0;
if(nums.length==1) return nums[0];

int n=nums.length;
int[] dp=new int[n];
dp[0]=nums[0];
dp[1]=nums[1];

for(int i=2;i<n;i++){
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[i-1];

总结

理解一维动态规划的本质是:用数组记录子问题的答案。通过记录下来 的答案来获得父问题。

也就是说难点在于找出特征方程。

对于本题来说:每一遍历到一个房子i,都面临两个选项;1.抢(代表前一个房子不能抢)2.不抢(抢前一个房子)

dp[i]=max(dp[i-2]+nums[i],dp[i-1])

300:最长递增子序列

题目要求:

给定一个整数数组nums,找到其中最长严格递增子序列长度

核心思路

先定义dp[i]=以nums[i]结尾的最长递增子序列长度

状态转移方程:dp[i]=max(dp[j]+1) for all j < i and nums[j] < nums[i]

对于每个nums[i],找前面所有比它小的nums[j]

初始化dp[i]=1

代码实现

java 复制代码
if(nums.length==0||nums==null) return 0;
int n=nums.length;
int[] dp=new int[n];
int maxLen=1;
Arrays.fill(dp,1)

for(int i=1;i<n;i++){
for(int j=1;j<n;j++){
if(nums[i]>nums[j]{
dp[i]=Math.max(dp[i],dp[j+1]);
}
}
mxaLen=Math.max(maxLen,dp[i]);
}

总结

本题最具有难度的是想到通过i和j两个变量,一个前一个后来判断递增数组是否增加,命dp[i]为以nums[i]为结尾的最长递增子序列长度

状态转移方程:dp[i] = max(dp[j] + 1) for all j < i and nums[j] < nums[i]

139:单词拆分

题目要求:给定一个字符串s和一个单词字典wordDict

要求:判断是否可以用字典里的单词完全拼接成s。

核心思路

动态规划定义状态:dp[i]=true/false表示s[0...i-1]是否可以被字典拆分

状态转移方程:dp[i]=dp[j]&&wordDict.contains(s[j...i-1])

代码实现

java 复制代码
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> wordSet=new HashSet<>(wordDict);//查找O(1)
        int n=s.length();
        boolean[] dp=new boolean[n+1];
        dp[0]=true;//空字符串可拆分
        
        for(int i=1;i<=n;i++){//子串长度
            for(int j=0;j<i;j++){
                if(dp[j]&&wordSet.contains(s.substring(j,i))){
                    dp[i]=true;
                    break;//找到一个拆分即可
                }
            }
        }
        return dp[n];
    }
}
相关推荐
bnmoel1 小时前
数据结构深度剖析栈与队列:结构、边界实现与进出操作全解析
c语言·数据结构·算法··队列
WL_Aurora1 小时前
Python 算法基础篇之查找算法(一):顺序查找、二分查找与插值查找
开发语言·python·算法
ChoSeitaku2 小时前
06_可变参数_递归_类和对象_封装
java·数据结构·算法
-To be number.wan2 小时前
算法日记 | 动态规划(初级)
算法·动态规划
_深海凉_2 小时前
LeetCode热题100-二叉搜索树中第 K 小的元素
算法·leetcode·职场和发展
图码2 小时前
文本两端对齐算法详解:从LeetCode到实际应用
数据结构·图像处理·算法·leetcode·生成对抗网络·面试·职场和发展
liu****2 小时前
第16届国赛蓝桥杯大赛C/C++大学C组
c语言·数据结构·c++·算法·蓝桥杯
沈浩(种子思维作者)2 小时前
物理的本质是数学,还是数学只是描述物理的方便之语?
人工智能·python·算法
黎阳之光2 小时前
数智孪生,全景可视——黎阳之光透明仓库,重构智慧仓储新范式
大数据·人工智能·算法·安全·数字孪生