[不更了][算法][动态规划][dynamic programing]力扣dp学习计划题单
最近开始跟着力扣的官方题单开始做题,先从动态规划开始做起,以后在此记录每周做的题目,做总结。
基本思路
动态规划利用递推或递归来解决问题,通常这个问题可以被拆分成相同的小问题,我们通过解决一个小问题继而解决更高一层的较大问题,整合其结果一直到原问题上。例如,斐波那契数列就是一个很典型的可以用动态规划解决的问题,因为其问题f(n)的值与其前两个值相关,因此这个问题就可以被拆分为f(i) = f(i-1) + f(i-2)并已知两个初值f(0), f(1)通过递推算出f(n)。
通常我们可以通过建表来记录f(i)的值,直到n>=i时。
int p=0, q=0, r=1; for (int i=2; i <= n; ++i) { p = q; // assign q value to p q = r; // assign r value to q r = p + q; // f(n) = f(n-1)+f(n-2) } return r;
打劫问题(House Robber/Delete & Earn)
这类问题中的f(n)往往根据不同的情况有不同的算式,也就是说对任意值i,f(i)是或不是组成大问题的一部分,也就是在计算f(i)时判断是包含它还是跳过它。例如,打劫问题中小偷不能偷连续的房子,那么对于任意一栋房子,它可以是被偷(nums[i]+houses[i-2])或跳过(houses[i-1]):
houses[0] = nums[0]; houses[1] = max(nums[0], nums[1]); for (int i=2; i<n; ++i) { houses[i] = max(nums[i]+houses[i-2], houses[i-1]); } return houses[n-1];
同理在delete & earn问题中,所有的某任意值i之前一个数m和之后一个数n会被删除,因此除了判断f(i)是保留还是跳过以外,还需要判断m和n与i的关系:
- 求较大值
- 保留:countElem[unique_elem[i]]+totalPts[i-2]
- 跳过:totalPts[i-1]
- 不是连续值
- countElem[unique_elem[i]] + totalPts[i-1]全加起来了就完事了
PS. totalPts[]存储各元素对应的最大点数; countElem[]存储原数组中重复元素的总和; unique_elem[]整合了原数组的元素,去掉重复部分。
- countElem[unique_elem[i]] + totalPts[i-1]全加起来了就完事了
for (int i=2; i<n; ++i) { totalPts[i] = (unique_elem[i]==unique_elem[i-1]+1)? (max(countElem[unique_elem[i]]+totalPts[i-2], totalPts[i-1])): (countElem[unique_elem[i]] + totalPts[i-1]); } return totalPts[n-1];
跳格子&最大子数组和(jump game/maximum subarray)
其实思路和打劫大差不差(只是我太菜了),都是判断元素组的某一元素/格子/字符是包含还是跳过,判断依据都是根据题意找最大/最小值,而且不需要建表,而是使用指针表示现在的位置/值,并在每一次循环中依据题意更新指针,再更新最大/最小值。
Max Subarray
// max subarray int maxVal = nums[0], curMax = 0; for (int& i:nums) { curMax = max(i, curMax+i); maxVal = max(maxVal, curMax); // update maxVal } return maxVal;
jump game
// same idea as jump game // to find the max jump length at [i, maxIndex] to decide the next position int maxIndex = 0, minJumps = 0, currentStop = 0, nextStop = 0; int n = nums.size(); if (n==1) return 0; // only one step, no jumps required while (nextStop < n-1) { // jumping // find the maxIndex it can reach maxIndex = max(maxIndex, currentStop+nums[currentStop]); // if the current stop reach to the end of range if (currentStop == nextStop) { ++minJumps; // increment jumps nextStop = maxIndex; // update the next stop } ++currentStop; // update current stop } return minJumps;
环形数组求最大数组和(jump game II)
重点是确定范围,展开数组,根据情况重组,记住数组总和totalSum是固定值。
- 若最大数组在原数组中间,则情况与跳跃游戏 I类似,只求maxVal;
- 若最大数组在头尾交界处,则有一个最小数组在中间,即minVal = totalSum - maxVal.
因此最大与最小,总和都需要计算。
注意:当原数组只包含负数时,根据题意子数组不能为空,则maxVal = max(array), minVal = sum(array) = total。于是最后判断max(maxVal, total - minVal)时,返回0而不是maxVal,因此需要再加一层判断。
circular max subarray
int maxVal = nums[0], minVal = nums[0], curMax = 0, curMin = 0, total = 0; if (nums.size()==1) return nums[0]; for (int& i:nums) { curMax = max(i, curMax+i); // update current maximum curMin = min(i, curMin+i); // update current minimum maxVal = max(curMax, maxVal); // update maxVal minVal = min(curMin, minVal); // update minVal total += i; // update total sum } // in case that nums[] contains only negative numbers return (minVal == total) ? maxVal : max(maxVal, total - minVal);
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~