LeetCode 2731. 移动机器人【脑筋急转弯,排序,前缀和】1922

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

有一些机器人分布在一条无限长的数轴上,他们初始坐标用一个下标从 0 开始的整数数组 nums 表示。当你给机器人下达命令时,它们以每秒钟一单位的速度开始移动。

给你一个字符串 s ,每个字符按顺序分别表示每个机器人移动的方向。'L' 表示机器人往左或者数轴的负方向移动,'R' 表示机器人往右或者数轴的正方向移动。

当两个机器人相撞时,它们开始沿着原本相反的方向移动。

请你返回指令重复执行 d 秒后,所有机器人之间两两距离之和。由于答案可能很大,请你将答案对 10^9 + 7 取余后返回。

注意:

  • 对于坐标在 i 和 j 的两个机器人,(i,j) 和 (j,i) 视为相同的坐标对。也就是说,机器人视为无差别的。
  • 当机器人相撞时,它们 立即改变 它们的前进方向,这个过程不消耗任何时间。
  • 当两个机器人在同一时刻占据相同的位置时,就会相撞。
    • 例如,如果一个机器人位于位置 0 并往右移动,另一个机器人位于位置 2 并往左移动,下一秒,它们都将占据位置 1,并改变方向。再下一秒钟后,第一个机器人位于位置 0 并往左移动,而另一个机器人位于位置 2 并往右移动。
    • 例如,如果一个机器人位于位置 0 并往右移动,另一个机器人位于位置 1 并往左移动,下一秒,第一个机器人位于位置 0 并往左行驶,而另一个机器人位于位置 1 并往右移动。

示例 1:

输入:nums = [-2,0,2], s = "RLL", d = 3
输出:8
解释:
1 秒后,机器人的位置为 [-1,-1,1] 。现在下标为 0 的机器人开始往左移动,下标为 1 的机器人开始往右移动。
2 秒后,机器人的位置为 [-2,0,0] 。现在下标为 1 的机器人开始往左移动,下标为 2 的机器人开始往右移动。
3 秒后,机器人的位置为 [-3,-1,1] 。
下标为 01 的机器人之间距离为 abs(-3 - (-1)) = 2 。
下标为 02 的机器人之间的距离为 abs(-3 - 1) = 4 。
下标为 12 的机器人之间的距离为 abs(-1 - 1) = 2 。
所有机器人对之间的总距离为 2 + 4 + 2 = 8

示例 2:

输入:nums = [1,0], s = "RL", d = 2
输出:5
解释:
1 秒后,机器人的位置为 [2,-1]2 秒后,机器人的位置为 [3,-2] 。
两个机器人的距离为 abs(-2 - 3) = 5

提示:

  • 2 <= nums.length <= 10^5
  • -2 * 10^9 <= nums[i] <= 2 * 10^9
  • 0 <= d <= 10^9
  • nums.length == s.length
  • s 只包含 'L' 和 'R' 。
  • nums[i] 互不相同。

解法 脑筋急转弯+排序+前缀和

提示 1
题目最后要求机器人对之间的距离和,此时把任意两个机器人的位置交换,并不会对答案产生影响。

假设 d d d 秒后机器人的位置数组为 [ 1 , 2 , 3 ] [1,2,3] [1,2,3] ,那么交换成 [ 2 , 1 , 3 ] [2,1,3] [2,1,3] ,所有机器人之间两两距离之和保持不变。既然如此,那么可以把机器人都看成是完全一样的,无法区分

提示 2
相撞则等价于机器人互相穿过对方,因为我们无法区分机器人。所以可以无视相撞的规则,把每个机器人都看成是独立运动的

类似的思路在1503. 所有蚂蚁掉下来前的最后一刻中出现过。

提示 3
d d d 秒后机器人的位置数组为 a a a ,根据提示 1,可以把数组 a a a 从小到大排序,再计算所有机器人之间两两距离之和

从小到大枚举 a [ i ] a[i] a[i] ,此时左边有 i i i 个数都不超过 a [ i ] a[i] a[i] ,那么 a [ i ] a[i] a[i] 与其左侧机器人的距离之和为:
( a [ i ] − a [ 0 ] ) + ( a [ i ] − a [ 1 ] ) + ⋯ + ( a [ i ] − a [ i − 1 ] ) =   i ⋅ a [ i ] − ( a [ 0 ] + a [ 1 ] + ⋯ + a [ i − 1 ] ) \begin{aligned} &(a[i] - a[0])+ (a[i] - a[1]) + \cdots + (a[i] - a[i-1])\\ =&\ i\cdot a[i] - (a[0] + a[1] + \cdots + a[i-1]) \end{aligned} =(a[i]a[0])+(a[i]a[1])++(a[i]a[i1]) ia[i](a[0]+a[1]++a[i1])
其中 a [ 0 ] + a [ 1 ] + ⋯ + a [ i − 1 ] a[0] + a[1] + \cdots + a[i-1] a[0]+a[1]++a[i1] 可以一边遍历 a a a ,一边计算出来。计算时,为了避免溢出,需要取模。这样做的正确性见下面的「算法小课堂:模运算」。

问:下面代码中,为什么不能对 a [ i ] a[i] a[i] 取模?
答:注意 a a a 中第 i i i 小的数要乘上 i i i ,取模后每个元素的大小关系就乱了,原来第 i i i 小的数要乘的就不一定是 i i i 了,所以会算出错误的结果。

class Solution {
public:
    int sumDistance(vector<int>& nums, string s, int d) {
        const int MOD = 1e9 + 7;
        int n = nums.size();
        vector<long long> a(n);
        for (int i = 0; i < n; ++i)
            a[i] = (long long) nums[i] + d * ((s[i] & 2) - 1); // L=-1,R=1
        sort(a.begin(), a.end());

        long long ans = 0, sum = 0;
        for (int i = 0; i < n; ++i) {
            ans = (ans + i * a[i] - sum) % MOD;
            sum += a[i];
        }
        return ans;
    }
};

复杂度分析:

  • 时间复杂度: O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn) ,其中 n n n nums \textit{nums} nums 的长度。瓶颈在排序上。
  • 空间复杂度: O ( n ) \mathcal{O}(n) O(n) O ( 1 ) \mathcal{O}(1) O(1) 。如果需要用一个新的 nums \textit{nums} nums 数组记录则需要 O ( n ) \mathcal{O}(n) O(n) 空间,否则为 O ( 1 ) \mathcal{O}(1) O(1)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

memcpy0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值