非常好的题解
三角形入门
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点 。
1 | '''方法1=深搜''' |
动态规划,自底向上
1 | class Solution: |
动态规划,自顶向下(不比自底向上优雅)
- 从上往下走,算到最后一层
- 由于下面一层比上面一层大,所以需要考虑左右的边界情况
- 然后遍历最后一层,找最小值返回
1 | class Solution: |
使用场景
满足两个条件
- 满足以下条件之一
- 求最大/最小值(Maximum/Minimum )
- 求是否可行(Yes/No )
- 求可行个数(Count(*) )
- 满足不能排序或者交换(Can not sort / swap )
四个要素
- 状态:存储小规模问题的结果
- 方程:状态之间的转移计算
- 初始化:开始的状态,可以把能算的先算了
- 答案:最后的状态在哪里?
常见四种类型
- Matrix DP (10%)
- Sequence (40%)
- Two Sequences DP (40%)
- Backpack (10%)
编写时注意事项
- 可以先创建 dp[n+1] [m+1] 然后,将 dp[0] [i] dp[i] [0]先设初值了
- 在算的时候,要做好条件
- 要注意看是什么类型的dp:矩阵、单序列、双序列、零花钱和背包?
1、矩阵类型(10%)
最小路径和
给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
示例:
1 | 输入: |
使用动态规划
- 由于每次只能向下或者向右移动一步,所以先初始化边缘值
- 然后从第二行开始,一行行往下刷新
- 也可以从第二列开始,一行行往下刷新(都能保证某点值依赖的点都备算过)
1 | class Solution: |
不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
例如,上图是一个7 x 3 的网格。有多少可能的路径?
解题思路:
- 初始化第一行、第一列为1,因为走到他们都只有一种走法
- 然后从第二行第二行开始遍历,每个走到这些方块的走法,由前两个方块的走法加起来
1 | class Solution: |
不同路径-ii
在上一题基础上,增加了障碍
网格中的障碍物和空位置分别用 1
和 0
来表示。
1 | class Solution: |
2、单序列类型(40%)
跳台阶
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
解:从前到后遍历计算
要注意的初值:0、1、2
1 | class Solution: |
跳跃游戏
给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
示例 1:
1 | 输入: [2,3,1,1,4] |
解:
- 遍历每个元素
- 对于一个元素,要遍历其前面所有元素才能判断
1 | class Solution: |
但是这样这道题会超时
我们更推荐下面的做法
1 | class Solution: |
跳跃游戏-ii ⭐
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
示例:
1 | 输入: [2,3,1,1,4] |
说明:
假设你总是可以到达数组的最后一个位置。
解法:还是用动态规划,超时了
1 | class Solution: |
于是我们挨着跳,这叫DP?
1 | class Solution: |
动态规划加贪心算法
1 | // v2 动态规划+贪心优化 |
分割回文串-ii
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。
说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。
1 | 输入: s = "leetcode", wordDict = ["leet", "code"] |
思路:
- 首选做初值,dp[0]=True,这样保证之后的可以好好遍历
- 最重要的是要有
dp[j] and s[j:i] in wordDict
这个条件的设置能力 - 然后要会计算
maxLen
,这个是wordDict中最长字符的长度,所以要遍历也要从i-maxLen
开始(如果i-maxLen不小于零的话)
1 | class Solution: |
最长上升子序列
序列类型,大同小异
要注意是这里的上升是严格上升,相同的不算上升
1 | class Solution: |
3、应是双字符串类型(矩阵) 双序列类型(40%)
主要用于两个字符串的比较
两个字符串组成矩阵
最长公共子序列
和背包问题差不多,但是有些难想到
需要有动态规划的思维
1 | class Solution: |
编辑距离
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
1 | class Solution: |
4、零钱与背包
零钱兑换 ⭐(虽说可用贪心,但要懂DP思路)
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
1 | 输入: coins = [1, 2, 5], amount = 11 |
主要是用一个长度为amount的dp数组来做
1 | class Solution: |
其实直接贪心就好了,何必动态规划
- 从大到小遍历coins,能装就装,装一个ans+1
- 最后判断target等不等于0
0-1背包问题(能装多少) ⭐
在 n 个物品中挑选若干物品装入背包,最多能装多满?假设背包的大小为 m,每个物品的大小为 A[i]
主要是要做一个矩阵,n个物品作为i , 背包大小m作为j
1 | func backPack (m int, A []int) int { |
0-1背包-ii 最大价值
有
n
个物品和一个大小为m
的背包. 给定数组A
表示每个物品的大小和数组V
表示每个物品的价值. 问最多能装入背包的总价值是多大?
思路:f[i] [j] 前 i 个物品,装入 j 背包 最大价值
做一个矩形的DP,然后
1 | def backPackII(m,A,V): |
go的版本
1 | func backPackII (m int, A []int, V []int) int { |
5、多序列类型
5.1 双序列类型
1 | class Solution: |
6.最长回文子串
1 | class Solution: |
剪绳子-1
1 | class Solution: |
剪绳子-2
1 | class Solution: |