动态规划总结

题目只要结果不要过程,并且有规律可寻,有重复子问题,这种类型可以使用动态规划。动态规划和回溯的区别是,回溯是要每个符合条件的结果,而动态规划一般都是只要最后或者最优的那个结果。

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

仅使用dp来做,递归其实也类似

由题目的意思可以理解为,爬楼梯有两种方法爬,一种是一次爬一级的方法,另一种是一次爬两级的方法

爬到第一级的楼梯  -> 用了一种方法

爬到第二级的楼梯  -> 用了两种方法

那爬到第三级楼梯  ==  爬到第二级楼梯方法的数 + 爬到第一级楼梯的方法数

爬到第四级楼梯 == 爬到第三级楼梯方法的数 + 爬到第二级楼梯的方法数

???

为什么会是这样的

这里可以这么理解

爬楼梯的方式有两种,一种是爬一个台阶,另一种是爬两个台阶

爬第四级阶梯的时候,我可以爬到第三级阶梯再爬一个台阶,也可以爬到第二级阶梯再爬两个台阶

这样就能得到dp公式了

F(n) = F(n-1) + F(n-2)

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

考虑特殊情况,我们从第三级阶梯开始爬,这样dp[0]为1 , dp[1]为2

class Solution {
public int climbStairs(int n) {
int[] dp = new int[n+1];
dp[0]=1;
dp[1]=2;
if(n==1||n==2)return dp[n-1];
for(int i=2;i<n+1;i++){
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n-1];
}
}

重复子问题

也叫「重叠子问题」。求解「斐波拉契数列」的例子就很好地展示了「重复子问题」。如果拆分问题的过程中没有发现「重复子问题」,使用「分而治之」或者「减而治之」的思想去解决就好了。

最优子结构

新阶段的、规模更大的问题最优解,可以参考已经解决的、规模较小的子问题的最优解得出。这样的性质称为「最优子结构」。

「动态规划」的方法广泛地应用于求解最优化问题,有一些问题可能不具有求解「最优」的要求,但是我们在求解问题的过程中,依然是用到了「较小规模的子问题的解构成了一个更大规模的问题的解」的性质,也称为这样的问题具有「最优子结构」。「斐波拉契数列」就是这样的例子。

无后效性

「无后效性」是很重要的概念。它的意思是:已经求得的子问题的结果不受后面阶段求解更大规模的问题的影响。下面我们具体解释这件事情

每一阶段求得的结果一旦计算出来,就不会再修改;

只记录每一阶段求解的结果,求解的具体步骤并不会被记录。

使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

列如 :输入:cost = [10,15,20]  输出:15   输入:cost = [1,100,1,1,1,100,1,1,100,1]  输出:6

分析一下题目思路,cost里面装的是每个阶梯所要的花费,要想到达顶部,可以选择走两步,或者是走一步,但是要最小的花费,那我对比一下二者谁小不就ok了,根据这个思路,我们可以写出dp公式

dp[ i ] = min( dp[ i -1 ] + cost[ i-1 ] , dp[ i -2 ] + cost[ i-2 ] )

因此,我们可以得到代码如下

class Solution {
public int minCostClimbingStairs(int[] cost) {
int []dp = new int[cost.length+1];
dp[0]=dp[1]=0;
for(int i=2;i<=cost.length;i++){
dp[i] = Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
}
return dp[cost.length];
}
}

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

列如:输入:[1,2,3,1]  输出:4   输入:[2,7,9,3,1] 输出:12

两种状态,一种是抢,另一种是不抢。抢的话就变成了num[ i  ] +dp[ i+2 ] 意味着,我抢了这间房子,然后走两步到达下一个,不抢的话,那就是dp[ i+1] ,如何判别抢不抢呢,那就判断谁现金多

我们写的时候优化一下,dp从2开始,所以代码如下

class Solution {
public int rob(int[] nums) {
int []dp = new int[nums.length+1];
dp[0]= 0;
dp[1]= nums[0];
int i =2;
while (i<nums.length+1){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
i++;
}
return dp[i-1];
}
}

打家劫舍2

这题对比上一个题目,多一个条件,那就是首尾相连,怎么解决

我们只要,抢头那就不抢尾,到尾房前一个停止,抢第二个房子,抢尾

因此,我们要做两次dp,然后来比较他们的大小,这次我们把dp内容封装到一个方法里面。

标记start和end

class Solution {
public int rob(int[] nums) {
if(nums.length==0) return 0;
if(nums.length==1)return nums[0];
if(nums.length==2)return Math.max(nums[1],nums[0]);
return Math.max(dpNum(nums,0,nums.length-1),dpNum(nums,1,nums.length));
}
public int dpNum(int[] nums,int start,int end){
int []dp = new int[nums.length+1];
dp[start]= 0;
dp[start+1]= nums[start];
int i =start+2;
while (i<end+1){
dp[i] = Math.max(dp[i-1],dp[i-2]+nums[i-1]);
i++;
}
return dp[i-1];
}
}

__EOF__

本文作者Reisen7
本文链接https://www.cnblogs.com/reisen7/p/18547110.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Reisen7  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示