LeetCode 算法:跳跃游戏 c++

题目

给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。

示例 1:
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

示例 2:
输入:nums = [3,2,1,0,4]
输出:false
解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。

提示:

  • 1 <= nums.length <= 104
  • 0 <= nums[i] <= 105

动态规划

动态规划(Dynamic Programming,简称DP)是一种算法思想,用于解决具有重叠子问题和最优子结构特性的问题。它通过将问题分解为更小的子问题,并存储这些子问题的解(通常是在表格中),来避免重复计算,从而提高算法效率。

动态规划的关键步骤:

  1. 识别子问题:将原问题分解为一系列子问题,这些子问题通常是原问题规模更小的版本。

  2. 确定状态:定义状态变量,通常用一个或多个变量来表示问题的某一阶段的状态。

  3. 状态转移方程:找到状态之间的关系,即当前状态是如何由之前的一个或多个状态推导出来的。

  4. 确定边界条件:确定递推的起始点,也就是基本情况(base case)。

  5. 计算顺序:确定计算状态的顺序,以确保在计算当前状态之前,所需的所有状态都已经被计算过。

  6. 构造最优解:根据状态转移方程和边界条件,从基本情况开始,逐步构建解决方案。

动态规划的类型:

  1. 自顶向下的递归方法(Memoization):从原问题开始,逐步分解为子问题,使用递归计算,并存储已经计算过的子问题的结果以避免重复计算。

  2. 自底向上的迭代方法:从基本情况开始,逐步向上计算直到原问题的状态,通常使用循环和表格来实现。

动态规划的应用领域:

  • 优化问题:如最短路径、最小花费、最大收益等。
  • 计数问题:计算不同选择的数量。
  • 组合问题:如切割问题、背包问题等。

示例:斐波那契数列(自底向上方法)

斐波那契数列是一个经典的动态规划问题,可以通过以下步骤解决:

  1. 状态定义F(n) 表示斐波那契数列的第 n 项。

  2. 状态转移方程F(n) = F(n-1) + F(n-2)

  3. 边界条件F(0) = 0F(1) = 1

  4. 计算顺序:从 n = 2n

  5. 代码实现

int fibonacci(int n) {
    if (n <= 1) return n;
    int dp[n + 1]; // 创建一个大小为 n+1 的数组
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; ++i) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
}

动态规划是一种强大的算法设计技术,适用于解决许多类型的问题。掌握动态规划需要大量的练习和对问题结构的深入理解。

贪心算法

贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法策略。贪心算法不保证会得到最优解,但在某些问题中,贪心算法的解是足够好的,或者在性能上是可接受的。

贪心算法的关键特点:

  1. 贪心选择性质:在每一步选择中都采取当前状态下的最优选择。
  2. 无回溯:一旦进行选择,就不再回溯,即不重新考虑之前的选择。
  3. 子问题最优:局部最优选择可能导致全局最优解。
  4. 可行性:选择的每一步必须是可行的。

贪心算法的适用场景:

  • 当问题具有最优子结构时,即问题的最优解包含子问题的最优解。
  • 当问题可以通过贪心选择性质来解决时。

贪心算法的步骤:

  1. 问题定义:明确问题的目标和约束条件。
  2. 贪心选择:确定每一步的贪心选择标准。
  3. 可行性检验:确保每一步的选择都是可行的。
  4. 构造解决方案:按照贪心选择逐步构造解决方案。

示例:硬币找零问题

假设有无限数量的硬币,面额分别为1元、2元和5元,需要找零金额为n元。贪心算法的思路是尽可能多地使用面额最大的硬币。

  1. 贪心选择:尽可能多地使用5元硬币。
  2. 状态更新:剩余金额减去5元硬币的面额。
  3. 重复:直到剩余金额小于5元。
  4. 继续贪心选择:然后尽可能多地使用2元硬币。
  5. 最终:使用1元硬币补足剩余金额。

代码实现:

#include <iostream>
#include <vector>

void coinChange(std::vector<int>& coins, int n) {
    int count = 0;
    for (int i = coins.size() - 1; i >= 0; --i) {
        count += n / coins[i];
        n %= coins[i];
    }
    std::cout << "Minimum coins needed: " << count << std::endl;
}

int main() {
    std::vector<int> coins = {1, 2, 5};
    coinChange(coins, 11); // 输出应该是 3 (5 + 5 + 1)
    return 0;
}

贪心算法简单且易于实现,但在使用时需要仔细考虑问题是否适合使用贪心策略。有些问题,如旅行商问题(TSP)或背包问题(Knapsack Problem),贪心算法可能无法得到最优解,而需要使用动态规划或其他更复杂的算法。

题解

  1. 解题思路:

LeetCode 上的 “跳跃游戏” 问题是一个典型的动态规划问题。这个问题的描述是:给定一个非负整数数组,表示每个位置可以跳跃的最大长度,判断从数组的开始位置出发,能否到达数组的最后一个位置。

  • 问题背景
    假设有一个数组 nums,其中 nums[i] 表示从位置 i 可以跳到 i + nums[i] 的位置。如果 i + nums[i] 大于或等于数组的长度,则表示可以跳出数组。

  • 解题思路

    • 贪心算法:这是解决这个问题的一种方法。我们可以从数组的开始位置开始,每次跳到能够到达的最远位置。然后,从这个新位置继续这个过程,直到到达或超过数组的最后一个位置。

    • 动态规划:另一种方法是使用动态规划。我们可以创建一个布尔数组 dp,其中 dp[i] 表示从位置 i 是否能够到达数组的末尾。然后,我们可以从后向前填充这个数组,因为我们知道从最后一个位置可以到达末尾。

    • 一次遍历:在贪心算法的基础上,我们可以进一步优化,只进行一次遍历。我们维护两个变量,maxReach 表示当前能够到达的最远位置,end 表示数组的末尾。在遍历过程中,如果当前位置加上可以跳的距离大于 maxReach,则更新 maxReach。如果 maxReach 大于或等于 end,则表示可以到达末尾。

  1. c++ demo:
#include <iostream>
#include <vector>

bool canJump(std::vector<int>& nums) {
    int maxReach = 0, i = 0;
    while (i < nums.size()) {
        if (i > maxReach) return false; // 如果当前索引超过了能到达的最远距离,则无法到达末尾
        maxReach = std::max(maxReach, i + nums[i]); // 更新能到达的最远距离
        i++; // 移动到下一个可以跳的位置
    }
    return true; // 如果遍历完成,说明可以到达末尾
}

int main() {
    std::vector<int> nums1 = { 2, 3, 1, 1, 4 };
    std::cout << (canJump(nums1) ? "true" : "false") << std::endl; // 应该输出 true

    std::vector<int> nums2 = { 3, 2, 1, 0, 4 };
    std::cout << (canJump(nums2) ? "true" : "false") << std::endl; // 应该输出 false

    std::vector<int> nums3 = { 0 };
    std::cout << (canJump(nums3) ? "true" : "false") << std::endl; // 应该输出 true

    return 0;
}
  1. 代码仓库地址: canJump
  • 21
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Codec Conductor

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值