Leetcode2008. 出租车的最大盈利

文章介绍了两种方法解决LeetCode题目2008出租车的最大盈利问题,一种是通过排序、二分查找和动态规划,另一种是结合哈希表优化状态转移过程。两种方法都分析了时间复杂度和空间复杂度。

Every day a Leetcode

题目来源:2008. 出租车的最大盈利

解法1:排序 + 二分查找 + 动态规划

将数组 rides 按照 endi 从小到大进行排序,记 m 为 rides 的大小,dp1 表示只接区间 [0,i] 内的乘客的最大盈利,显然 dp0 = 0,而对于 i∈[0,m],有两种情况:

  • 对第 i 位乘客接单:由于前面已经对数组 rides 排序,因此我们可以通过二分查找来找到满足 endj ≤ starti 最大的 j,那么,设 earningi-1 = rides[i - 1][1] - rides[i - 1][0] + rides[i - 1][2],有 dp[i] = max(dp[i - 1], dp[j] + earning)。
  • 不对第 i 位乘客接单:dp[i] = dp[i-1]。

于是,我们得到了状态转移方程:dp[i] = max(dp[i - 1], dp[j] + earning)

代码:

/*
 * @lc app=leetcode.cn id=2008 lang=cpp
 *
 * [2008] 出租车的最大盈利
 */

// @lc code=start
class Solution
{
private:
    // 对数组 rides 按 end 从小到大排序
    static bool cmp(const vector<int> &ride1, const vector<int> &ride2)
    {
        return ride1[1] < ride2[1];
    }

public:
    long long maxTaxiEarnings(int n, vector<vector<int>> &rides)
    {
        // 特判
        if (n == 0 || rides.empty())
            return 0;
        sort(rides.begin(), rides.end(), cmp);
        int m = rides.size();
        // dp[i]: 接区间 [0, i] 内的乘客的最大盈利
        vector<long long> dp(m + 1, 0);
        // 初始化
        dp[0] = 0;
        // 状态转移
        for (int i = 1; i <= m; i++)
        {
            int j = upper_bound(rides.begin(), rides.begin() + i - 1, rides[i - 1][0], [](int x, const vector<int> &r) -> bool
                                { return x < r[1]; }) -
                    rides.begin();
            int earning = rides[i - 1][1] - rides[i - 1][0] + rides[i - 1][2];
            dp[i] = max(dp[i - 1], dp[j] + earning);
        }
        return dp[m];
    }
};
// @lc code=end

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(mlogm),其中 m 是数组 rides 的长度。一次二分查找需要 O(log⁡m) 的时间,共进行 m 次二分查找,总计的时间复杂度为 O(mlogm)。

空间复杂度:O(m),其中 m 是数组 rides 的长度。

解法2:动态规划 + 哈希表

使用哈希表 rideMap[end] 记录终点为 end 的所有乘客信息。不同于解法 1,我们使用 dp[i] 表示到达第 i 个地点时,能获取的最大盈利,显然有 dp[0] = 0,而对于 i∈[1,n],有两种情况:

  1. 选择一个终点为第 i 个地点的乘客 j:设这次盈利 earning = endj - startj + tipj,那么最大盈利为 dp[i] = dp[startj] + earning。
  2. 没有乘客在第 i 个地点下车:dp[i] = dp[i-1]。

于是,我们得到了状态转移方程:dp[i] = max(dp[i - 1], max(dp[startj] + endj - startj + tipj)。

注意,因为 unordered_map 内部是由哈希表实现的,哈希表中的每一项为一个桶,分别装着键和值,原 STL 中不包含 pair<int,int> 类型的键,不能够直接使用嵌套到哈希表中,所以需要自定义哈希值与判断相等函数。

代码:

// 哈希表 + 动态规划

class Solution
{
public:
    long long maxTaxiEarnings(int n, vector<vector<int>> &rides)
    {
        // 特判
        if (n == 0 || rides.empty())
            return 0;
        unordered_map<int, vector<vector<int>>> rideMap; //<end, {start, tip}>
        for (const vector<int> &ride : rides)
        {
            int start = ride[0], end = ride[1], tip = ride[2];
            rideMap[end].push_back({start, tip});
        }
        // dp[i]: 到达第 i 个地点时能获取的最大盈利
        vector<long long> dp(n + 1, 0);
        // 初始化
        dp[0] = 0;
        // 状态转移
        for (int i = 1; i <= n; i++)
        {
            dp[i] = max(dp[i], dp[i - 1]);
            for (vector<int> &ride : rideMap[i])
            {
                int start = ride[0], end = i, tip = ride[1];
                int earning = end - start + ride[1];
                dp[i] = max(dp[i], dp[start] + earning);
            }
        }
        return dp[n];
    }
};

结果:

在这里插入图片描述

复杂度分析:

时间复杂度:O(m+n),其中 m 是数组 rides 的长度,n 是地点的数量。动态规划转移需要 O(n) 的时间,查询乘客信息需要 O(m) 的时间。

空间复杂度:O(m+n),其中 m 是数组 rides 的长度,n 是地点的数量。动态规划需要保存 n 个状态,哈希表需要保存 m 个乘客信息。

### 如何在 VSCode 中安装和配置 LeetCode 插件以及 Node.js 运行环境 #### 安装 LeetCode 插件 在 VSCode 的扩展市场中搜索 `leetcode`,找到官方提供的插件并点击 **Install** 按钮进行安装[^1]。如果已经安装过该插件,则无需重复操作。 #### 下载安装 Node.js 由于 LeetCode 插件依赖于 Node.js 环境,因此需要下载并安装 Node.js。访问官方网站 https://nodejs.org/en/ 并选择适合当前系统的版本(推荐使用 LTS 版本)。按照向导完成安装流程后,需确认 Node.js 是否成功安装到系统环境中[^2]。 可以通过命令行运行以下代码来验证: ```bash node -v npm -v ``` 上述命令应返回对应的 Node.js 和 npm 的版本号。如果没有正常返回版本信息,则可能未正确配置环境变量。 #### 解决环境路径问题 即使完成了 Node.js 的安装,仍可能出现类似 “LeetCode extension needs Node.js installed in environment path” 或者 “command ‘leetcode.toggleLeetCodeCn’ not found” 的错误提示[^3]。这通常是因为 VSCode 未能识别全局的 Node.js 路径或者本地安装的 nvm 默认版本未被正确加载[^4]。 解决方法如下: 1. 手动指定 Node.js 可执行文件的位置 在 VSCode 设置界面中输入关键词 `leetcode`,定位至选项 **Node Path**,将其值设为实际的 Node.js 安装目录下的 `node.exe` 文件位置。例如:`C:\Program Files\nodejs\node.exe`。 2. 使用 NVM 用户管理工具调整默认版本 如果通过 nvm 工具切换了不同的 Node.js 版本,请确保设置了默认使用的版本号。可通过以下指令实现: ```bash nvm alias default <version> ``` 重新启动 VSCode 后测试功能键是否恢复正常工作状态。 --- #### 配置常用刷题语言 最后一步是在 VSCode 设置面板中的 LeetCode 插件部分定义个人习惯采用的主要编程语言作为默认提交方式之一。这样可以减少频繁修改编码风格的时间成本。 --- ### 总结 综上所述,要在 VSCode 上顺利启用 LeetCode 插件及其关联服务,除了基本插件本身外还需额外准备支持性的后台框架——即 Node.js 应用程序引擎;同时针对特定场景下产生的兼容性障碍采取针对性措施加以修正即可达成目标[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

UestcXiye

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

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

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

打赏作者

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

抵扣说明:

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

余额充值