动态规划是一种思想,因此这里主要讲解动态规划相关的习题及应用。
区间调度问题1.无权区间调度问题
对于该问题,{b, e, h}是最大互相兼容的任务集合,这里需要使用贪心算法。
贪心算法:总是做出当前最优的选择,并不总能得到最优解,但是它是最简单最容易实现的算法。
无权区间调度问题贪心算法:先将任务以某种顺序排序,再按顺序挑选互相兼容的任务。
对于该问题,需要按照结束时间排序,时间复杂度是O(nlogn)
2.带权区间调度问题
动态规划四步骤:
确定状态:先按任务结束时间排序,定义状态OPT(j)为任务{1, 2, …, j}的最大权重,如
状态转移方程
初始条件和边界情况
确定计算顺序
在动态规划的实现中,可以使用自底向上(一般是for循环,易于理解)或者自顶向下(使用递归方式,可能可以节省一些不必要的计算)的方式实现。
补:在计算P(j)的时候,时间复杂度是O(n),需要将区间按照开始时间排序,和按照结束时间排序的结果,然后使用双指针计算P(j)即可。
背包问题对于该题,按照贪心算法行不通,需要使用动态规划解决。
动态规划步骤:
注意其中左边的方式行不通,因为没有考虑背包的容量问题。
题目解析 最大子数组和1.题目描述
题目链接
2.解析思路及代码
确定状态:OPT(i)表示以i结尾的连续子数组的最大和。
OPT(0) = nums[0]
OPT(i) = max(OPT(i - 1) + nums[i], nums[i])
public int maxSubArray(int[] nums) {
if (nums.length == 0) return 0;
int[] res = new int[nums.length];
res[0] = nums[0];
int ans = nums[0];
for (int i = 1; i < nums.length; i ++ ) {
res[i] = Math.max(res[i - 1] + nums[i], nums[i]);
ans = Math.max(ans, res[i]);
}
return ans;
}
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
res = []
res.append(nums[0])
for i in nums[1:]:
res.append(max(res[-1] + i, i))
return max(res)
最长公共子序列
1.题目描述
题目链接
2.解题思路及代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EIUsBlWO-1646490127700)(images/1007最长公共子序列状态转移方程.png)]
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m; i ++ ) {
char c1 = text1.charAt(i - 1);
for (int j = 1; j <= n; j ++ ) {
char c2 = text2.charAt(j - 1);
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if text1[i - 1] == text2[j - 1]:
dp[i][j] = dp[i - 1][j - 1] + 1
else:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
return dp[m][n]
买卖股票的最佳时机
1.题目描述
题目链接
2.解题思路及代码
暴力解法O(n ^ 2)找到最低点,计算当前节点和最低点的差值即可。
public int maxProfit(int[] prices) {
int min = Integer.MAX_VALUE;
int res = 0;
for (int price : prices) {
res = Math.max(res, price - min);
min = Math.min(min, price);
}
return res;
}
class Solution:
def maxProfit(self, prices: List[int]) -> int:
min_price = int(1e9)
res = 0
for price in prices:
res = max(res, price - min_price)
min_price = min(min_price, price)
return res
买卖股票的最佳时机 II
1.题目描述
题目链接
2.解题思路及代码
贪心:ans += Math.max(0, prices[i] - princes[i - 1])动态规划: d p [ i ] [ 0 ] = m a x ( d p [ i − 1 ] [ 0 ] , d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] ) d p [ i ] [ 1 ] = m a x ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 0 ] − p r i c e s [ i ] ) dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]) \dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]) dp[i][0]=max(dp[i−1][0],dp[i−1][1]+prices[i])dp[i][1]=max(dp[i−1][1],dp[i−1][0]−prices[i])
public int maxProfit(int[] prices) {
int res = 0;
for (int i = 1; i < prices.length; i ++ ) {
res += Math.max(0, prices[i] - prices[i - 1]);
}
return res;
}
class Solution:
def maxProfit(self, prices: List[int]) -> int:
res = 0
for i in range(1, len(prices)):
res += max(0, prices[i] - prices[i - 1])
return res
最长回文子序列
1.题目描述
题目链接
2.解题思路及代码
插入排序的思路,需要注意两点:
防止边界问题,使用带有头结点的链表在找当前节点需要插入的位置时,正常针对数组的插入排序从i - 1开始找,但是这是链表,需要从头节点开始找。
public int longestPalindromeSubseq(String s) {
int n = s.length();
int[][] dp = new int[n][n];
for (int i = n - 1; i >= 0; i -- ) {
dp[i][i] = 1;
char c1 = s.charAt(i);
for (int j = i + 1; j < n; j ++ ) {
char c2 = s.charAt(j);
if (c1 == c2) {
dp[i][j] = dp[i + 1][j - 1] + 2;
} else {
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
}
return dp[0][n - 1];
}
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
n = len(s)
dp = [[0] * n for _ in range(n)]
for i in range(n - 1, -1, -1):
dp[i][i] = 1
for j in range(i + 1, n):
if s[i] == s[j]:
dp[i][j] = dp[i + 1][j - 1] + 2
else:
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])
return dp[0][n - 1]



