【LeetCode】动态规划—2140. 解决智力问题(附完整Python/C++代码)

题目描述

在这里插入图片描述

前言

解决智力问题 是一个典型的动态规划问题。给定一个数组 questions,其中 questions[i] = [points_i, brainpower_i] 表示解决第 i 个问题可以获得的积分是 points_i,但解决它后你必须跳过接下来的 brainpower_i 个问题。目标是通过选择一系列问题,最大化你可以获得的总积分。


基本思路

1. 问题定义

给定一个问题数组 questions,每个问题由两个数值组成:points_ibrainpower_i,表示解决当前问题能得到的积分以及跳过的题目数量。你需要从左到右遍历题目,选择一系列问题以最大化总积分。

举例:

  • 输入:questions = [[3, 2], [4, 3], [4, 4], [2, 5]]
  • 输出:5
  • 解释:
    • 选择第一个问题(积分 3),然后跳过下两个问题。
    • 然后选择第四个问题(积分 2),总积分为 3 + 2 = 5

2. 理解问题和递推关系

动态规划思想:

  • 我们从右向左遍历问题。对于每个问题,我们有两种选择:
    1. 选择当前问题:那么我们可以获得它的积分,但接下来需要跳过 brainpower_i 个问题。
    2. 不选择当前问题:则我们直接考虑下一个问题。

状态定义:

  • dp[i] 表示从第 i 个问题开始能获得的最大总积分。

状态转移方程:

  • 对于每个问题 i,可以选择不解决或者解决它:
    1. 不解决问题 i:则 dp[i] = dp[i + 1]
    2. 解决问题 i:则 dp[i] = points[i] + dp[i + brainpower[i] + 1],如果跳过后的索引超出了数组长度,则积分为 0。
  • 最终我们取两者中的最大值:
    d p [ i ] = max ⁡ ( d p [ i + 1 ] , p o i n t s i + d p [ i + b r a i n p o w e r i + 1 ] ) dp[i] = \max(dp[i + 1], points_i + dp[i + brainpower_i + 1]) dp[i]=max(dp[i+1],pointsi+dp[i+brainpoweri+1])

边界条件:

  • 当我们到达最后一个问题时,dp[i] 只等于当前问题的积分 points_i,因为后面已经没有问题可跳过了。

3. 解决方法

动态规划方法

  1. 初始化:创建一个数组 dp,其中 dp[i] 表示从第 i 个问题开始可以获得的最大积分。
  2. 状态转移:从右到左遍历问题,通过状态转移方程计算每个 dp[i] 的值。
  3. 返回结果dp[0] 即为从第一个问题开始可以获得的最大积分。

伪代码:

initialize dp array with dp[len(questions)] = 0
for i from len(questions) - 1 to 0:
    points_i = questions[i][0]
    brainpower_i = questions[i][1]
    dp[i] = max(dp[i + 1], points_i + dp[i + brainpower_i + 1])
return dp[0]

4. 进一步优化

  • 时间复杂度:时间复杂度为 O(n),其中 n 是问题的数量,因为我们需要遍历每个问题。
  • 空间复杂度:空间复杂度为 O(n),因为我们需要维护一个 dp 数组。

可以进一步优化空间,将 dp 数组简化为两个变量,减少空间复杂度为 O(1)

5. 小总结

  • 递推思路:我们通过自右向左遍历问题,并使用动态规划数组 dp 来记录从每个问题开始的最大积分。最后返回 dp[0]
  • 时间复杂度:时间复杂度为 O(n),空间复杂度可以优化为 O(1)

以上就是解决智力问题的基本思路。


Python代码

class Solution:
    def mostPoints(self, questions: list[list[int]]) -> int:
        n = len(questions)
        dp = [0] * (n + 1)  # dp[i] 表示从第i个问题开始能获得的最大积分

        # 从右到左遍历每个问题
        for i in range(n - 1, -1, -1):
            points, brainpower = questions[i]
            # 选择不做问题i 或 做问题i
            dp[i] = max(dp[i + 1], points + dp[min(i + brainpower + 1, n)])
        
        return dp[0]  # 返回从第0个问题开始的最大积分

Python代码解释

  1. 初始化:定义一个 dp 数组,dp[i] 表示从第 i 个问题开始能获得的最大积分。
  2. 状态转移:从右向左遍历问题,更新每个 dp[i],比较选择做或不做当前问题的最大积分。
  3. 返回结果:返回 dp[0],即从第一个问题开始的最大积分。

C++代码

class Solution {
public:
    long long mostPoints(vector<vector<int>>& questions) {
        int n = questions.size();
        vector<long long> dp(n + 1, 0);  // dp[i] 表示从第i个问题开始能获得的最大积分

        // 从右到左遍历每个问题
        for (int i = n - 1; i >= 0; --i) {
            int points = questions[i][0];
            int brainpower = questions[i][1];
            // 选择不做问题i 或 做问题i
            dp[i] = max(dp[i + 1], points + dp[min(i + brainpower + 1, n)]);
        }

        return dp[0];  // 返回从第0个问题开始的最大积分
    }
};

C++代码解释

  1. 初始化:定义一个 dp 数组,dp[i] 表示从第 i 个问题开始能获得的最大积分。
  2. 状态转移:从右向左遍历问题,计算并更新 dp[i] 的值,选择做或不做当前问题的最大积分。
  3. 返回结果:返回 dp[0],即从第一个问题开始的最大积分。

总结

  • 核心思路:这是一个典型的动态规划问题。通过自右向左遍历问题,可以使用 dp 数组记录从每个问题开始能获得的最大积分,从而解决问题。
  • 时间复杂度:时间复杂度为 O(n),适合处理中等规模的输入。
  • 空间优化:可以进一步优化空间复杂度,将 dp 数组简化为两个变量,减少空间占用到 O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Albert_Lsk

今天又能喝柠檬茶啦

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

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

打赏作者

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

抵扣说明:

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

余额充值