LeetCode Day 动态规划第 9 部分

在算法的世界里,动态规划是一种强大的解决问题的方法。它常用于优化可以分阶段进行的小问题,以最终达到解决整个问题的目的。在LeetCode上,动态规划的题目种类丰富,挑战性十足。本篇文章将探讨动态规划的第九部分,着重分析几道经典题目和解法。

动态规划的基本概念

动态规划(Dynamic Programming,简称DP)是一种将复杂问题分解为简单子问题的策略。与递归不同,动态规划通过记忆化的去存储中间结果,从而避免重复计算,提升效率。动态规划主要包括两个步骤:

定义状态

首先需要明确DP状态的定义,通常为了便于理解,我们会使用一个数组dp[]来表示某个状态下的结果。例如,在求斐波那契数列时,可以定义dp[i]为第i个斐波那契数。

状态转移方程

接下来,需要设计一个状态转移方程,描述如何从已知状态推导出未知状态。对于斐波那契数列,状态转移方程为:

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

经典动态规划题目分析

以下是几道LeetCode中经典的动态规划题目,适合深入理解该算法的实现。

1. 最长上升子序列

题目描述:给定一个整数数组,求出最长上升子序列的长度。

状态定义:设dp[i]为以nums[i]结尾的最长上升子序列的长度。

状态转移方程:

dp[i] = max(dp[j] + 1) 其中 0 <= j < i 且 nums[j] < nums[i];

最终结果为max(dp)的值。

int lengthOfLIS(int[] nums) {

if (nums.length == 0) return 0;

int[] dp = new int[nums.length];

Arrays.fill(dp, 1);

for (int i = 1; i < nums.length; i++) {

for (int j = 0; j < i; j++) {

if (nums[j] < nums[i]) {

dp[i] = Math.max(dp[i], dp[j] + 1);

}

}

}

return Arrays.stream(dp).max().getAsInt();

}

2. 零钱兑换

题目描述:给定不同面额的硬币和一个总金额,求组合数,返回能组合出总金额的最小硬币数。

状态定义:dp[i]表示组成金额i所需的最小硬币数。

状态转移方程:

dp[i] = min(dp[i - coin] + 1) 其中 coin 为可用硬币。

int coinChange(int[] coins, int amount) {

int[] dp = new int[amount + 1];

Arrays.fill(dp, amount + 1);

dp[0] = 0;

for (int i = 1; i <= amount; i++) {

for (int coin : coins) {

if (i - coin >= 0) {

dp[i] = Math.min(dp[i], dp[i - coin] + 1);

}

}

}

return dp[amount] == amount + 1 ? -1 : dp[amount];

}

动态规划的复杂度分析

动态规划的时间复杂度一般由两个部分决定:状态数和每个状态的时间复杂度。对于前面提到的最长上升子序列,时间复杂度为O(N^2),而对于零钱兑换,时间复杂度也是O(N*M),其中N为目标金额,M为硬币种类数。

总结

通过以上题目的分析,我们可以看到动态规划在解决最优化问题中的巨大潜力。无论是简单的子序列问题,还是组合优化问题,掌握动态规划都能帮助我们在解决问题时事半功倍。希望通过LeetCode的练习,大家能更深入地理解动态规划这门经典算法。继续努力,相信在动态规划的道路上,你会有更大的收获!

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签