动态规划问题的常规解法
前言:最近刷剑指offer刷到了动态规划相关的问题,之前没怎么学过,所以特地抽一天时间来学一下,以下为学习的笔记
动态规划
题目类型
1. 计数:
有多少种方式走到右下角
有多少种方法选出k个数使得和为Sum
2. 求最大最小值:
从左上角走到右下角路径的最大数字和
最长上升子序列长度
3. 求存在性:
取石子游戏,先手是否必胜
能不能选出k个数使得和是Sum
解题步骤
- 确定状态
简单的说,就是解动态规划时需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么,类似解数学题中,xyz代表什么一样,具体分为下面两个步骤:- 研究最优策略的最后一步
- 化为子问题
- 转移方程
根据子问题定义直接得到 - 初始条件和边界情况
初始条件一般都是a[0]、a[1]这种,多看看
边界条件主要是看数组的边界,数组越不越界 - 计算顺序
大部分从小到大迭代,精髓在于使用之前计算得到的结果
322. 零钱兑换
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
思路与题解
代码
1 | /* |
62. 不同路径
难度 中等
一个机器人位于一个 m x n
网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
示例 1:
1 | 输入:m = 3, n = 7 |
示例 2:
1 | 输入:m = 3, n = 2 |
示例 3:
1 | 输入:m = 7, n = 3 |
示例 4:
1 | 输入:m = 3, n = 3 |
提示:
1 <= m, n <= 100
- 题目数据保证答案小于等于
2 * 109
思路与题解
转移方程:
初始状态:
代码
C++二维数组开辟方法
这里还学到了点C++的二维数组的开辟方法,以开辟m行,n列的二维数组 array2D[m][n]
方法总结如下:
-
n为已知常量;二维数组开辟时第二位不能为变量,因此需要n已知且为常量才能使用
假设 n = 5;则可开辟array2D[m][5]
-
使用指针间接引用;即先开辟 m 个指向指针的指针``array2D`再在每一行开辟新的行数组
1 | int **array2D = new int *[m]; |
- 使用STL中的vector容器
1 | typedef std::vector<int> IntVector; |
解题代码
1 | /* |
55. 跳跃游戏
难度 中等
给定一个非负整数数组 nums
,你最初位于数组的 第一个下标 。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标。
示例 1:
1 | 输入:nums = [2,3,1,1,4] |
示例 2:
1 | 输入:nums = [3,2,1,0,4] |
提示:
1 <= nums.length <= 3 * 104
0 <= nums[i] <= 105
思路
其实这题并不适合拿动态规划来解,因为写出来的时间复杂度有数量级(所以我的题解在LeetCode里因为超时挂了。。。不过还是作为一个DP的典型题目写下思路
代码
1 | class Solution { |