动态规划
贪心算法
每一步都寻求最优解,最终结果也是最优解.
跳跃游戏
[跳跃游戏,采用贪心算法](55. 跳跃游戏 - 力扣(LeetCode))
function getJump(arr){
//最远能到达的距离
let maxReach=0
//将maxReach暂时存储在end
let end=0
//步长
let step=0
for(let i=0;i<arr.length-1;i++){
maxReach=Math.max(maxReach,i+arr[i])
if(i==end){
step++
end=maxReach
}
}
return step++
}`
跳跃游戏2
[采用贪心算法](45. 跳跃游戏 II - 力扣(LeetCode))
`var canJump = function(nums) {
let step=0;
let end=0;
let maxReach=0
for(let i=0;i<nums.length-1;i++){
maxReach=Math.max(maxReach,i+nums[i])
if(i==end){
end=maxReach
}
}
if(end>=nums.length-1){
return true
}
else{
return false
}
};`
总结
两道题都采用了贪心算法,每一步都找一个最远到达距离,然后通过max方法来确定下一步到达的最远距离。 注意:下一步的最远距离不是由上一步的最远距离来决定的,所以将最远距离存储在end中很有必要
动态规划
1.(53. 最大子数组和 - 力扣(LeetCode))
总结:
动态规划题的步骤:
1.定义状态,找到最理想的子状态,如本题最理想的子状态就是以数组中某个数结尾的子数组。
第一个子状态:以-2结尾的子数组
第二个子状态:以1结尾的子数组
第三个子状态:以-3结尾的子数组
......
每个子状态的最优解(除了第一个是他本身nums[0])都是前一个的最优解+本身nums[i],或者就是本身nums[i]
2.写转移方程式
子状态确定好了后,就该写转移方程了。
//如果前面那个状态为负数,就抛弃那个状态,否则,就加上。因为题目求的是最大的和。
//所有阻止成为最大和的状态都应该被抛弃
if(dp[i-1)<0){
dp[i]=nums[i]
}
if(dp[i-1)>0){
dp[i]=dp[i-1]+nums[i]
}
完整代码如下:
var maxSubArray = function(nums) {
//定义状态
//定义子问题为以nums[i]结尾的子数组
//状态转移方程
//dp[i]=dp[i-1]+nums[i]
//方法一
// //初始化
// let dp=[]
// let max=nums[0]
// for(let i=0;i<nums.length;i++){
// dp[0]=nums[0]
// if(dp[i-1]<0){
// dp[i]=nums[i]
// }
// else{
// dp[i]=dp[i-1]+nums[i]
// }
//这一步是上面if-else的优化
// //dp[i]=Math.max(dp[i-1]+nums[i],nums[i])
//将最大的子状态存储起来
// if(dp[i]>max){
// max=dp[i]
// }
// }
//输出
// return max
//方法二:节省空间。不用新建数组,直接将结果存在数组里面
let max=nums[0]
if(nums.length==1){
return nums[0]
}
for(let i=1;i<=nums.length-1;i++){
nums[i]+=Math.max(nums[i-1],0)
max=Math.max(max,nums[i])
}
return max
};
####################################
2.(62. 不同路径 - 力扣(LeetCode))
题目:
分析:
1.定义子状态:到达终点的过程,每一步就是一个子状态。除了边界外,每个格子的路径总和都等于左格子路径总和加上右格子路径总和。
2.状态转移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1]
3.边界问题:不难看出,矩阵的第一排和第一列就是这道题的边界问题。因为机器人只能向下向右,所以矩阵的第一排和第一列都是一个路径可以到达。所以dp[i][j]=1
代码:
var uniquePaths = function(m, n) {
let f=new Array(m).fill(0).map(()=>{
return new Array(n).fill(0)
})
//定义状态
//每一步的路径等于x-1,y这步的路径+x,y-1这步的路径
//当x=0||y=0时,路径都只有一种,因为机器人只能向右向下走
//状态转移方程
//dp[x][y]=dp[x-1][y]+dp[x][y-1]
//初始化
for(let x=0;x<m;x++){
for(let y=0;y<n;y++){
if(x==0||y==0){
f[x][y]=1
}
else{
f[x][y]=f[x-1][y]+f[x][y-1]
}
}
}
return f[m-1][n-1]
};
分析:与2大同小异,还是最优子状态,边界问题和状态转移方程的推导
代码:
var minPathSum = function(dp) {
//到目的地路径和最小,则到最近两个位置的路径和最小
//dp[x][y]+=Math.min(dp[x-1][y],dp[x][y-1])
//临界条件
for(let x=0;x<dp.length;x++){
for(let y=0;y<dp[0].length;y++){
if(x==0&&y==0){
continue;
}
if(x==0){
dp[x][y]+=dp[x][y-1]
}
if(y==0){
dp[x][y]+=dp[x-1][y]
}
if(x!=0&&y!=0){
dp[x][y]+=Math.min(dp[x-1][y],dp[x][y-1])
}
}
}
return dp[dp.length-1][dp[0].length-1]
//关键在于找到每一步的最小值,只要求输出总和
};
总结
1.动态规划的题,一般会出现“连续”,“最”这样的字。
2.解动态规划的题,一般步骤是定义最优子状态,写状态转移方程,边界问题。边界问题最容易被忽视,要考虑到多重条件!!!