LeetCode第196场周赛题解

LeetCode第196场周赛题解

题目来源:LeetCode官网


1.LeetCode 1502. 判断能否形成等差数列

题目描述

给你一个数字数组 arr 。
如果一个数列中,任意相邻两项的差总等于同一个常数,那么这个数列就称为 等差数列 。
如果可以重新排列数组形成等差数列,请返回 true ;否则,返回 false 。

示例 1:
输入:arr = [3,5,1]
输出:true
解释:对数组重新排序得到 [1,3,5] 或者 [5,3,1] ,任意相邻两项的差分别为 2 或 -2 ,可以形成等差数列。
示例 2:
输入:arr = [1,2,4]
输出:false
解释:无法通过重新排序得到等差数列。

提示:

  • 2 <= arr.length <= 1000
  • -106 <= arr[i] <= 106

分析

题目不难,直接上代码

代码

class Solution {
public:
    bool canMakeArithmeticProgression(vector<int>& arr) {
        sort(arr.begin(), arr.end());
        int sub = arr[1] - arr[0];
        for(int i = 1; i < arr.size() - 1; i++)
            if(arr[i + 1] - arr[i] != sub)
                return false;
        return true;
    }
};

2.LeetCode 1503. 所有蚂蚁掉下来前的最后一刻

题目描述

有一块木板,长度为 n 个 单位 。一些蚂蚁在木板上移动,每只蚂蚁都以 每秒一个单位 的速度移动。其中,一部分蚂蚁向 左 移动,其他蚂蚁向 右 移动。
当两只向 不同 方向移动的蚂蚁在某个点相遇时,它们会同时改变移动方向并继续移动。假设更改方向不会花费任何额外时间。
而当蚂蚁在某一时刻 t 到达木板的一端时,它立即从木板上掉下来。
给你一个整数 n 和两个整数数组 left 以及 right 。两个数组分别标识向左或者向右移动的蚂蚁在 t = 0 时的位置。请你返回最后一只蚂蚁从木板上掉下来的时刻。

示例 1:
在这里插入图片描述
输入:n = 4, left = [4,3], right = [0,1]
输出:4
解释:如上图所示:
-下标 0 处的蚂蚁命名为 A 并向右移动。
-下标 1 处的蚂蚁命名为 B 并向右移动。
-下标 3 处的蚂蚁命名为 C 并向左移动。
-下标 4 处的蚂蚁命名为 D 并向左移动。
请注意,蚂蚁在木板上的最后时刻是 t = 4 秒,之后蚂蚁立即从木板上掉下来。(也就是说在 t = 4.0000000001 时,木板上没有蚂蚁)。

提示:

  • 1 <= n <= 10^4
  • 0 <= left.length <= n + 1
  • 0 <= left[i] <= n
  • 0 <= right.length <= n + 1
  • 0 <= right[i] <= n
  • 1 <= left.length + right.length <= n + 1
  • left 和 right 中的所有值都是唯一的,并且每个值 只能出现在二者之一 中。

分析

  • 算法: 模拟
  • 时间复杂度: o(n)
  • 基本思路:
  1. 两只蚂蚁相遇转向可看做两只蚂蚁互相穿过
  2. 分别统计向右走的最左边的蚂蚁和向左走的最右边的蚂蚁从木板上掉下来的最后一刻,答案取两者较大值

代码

class Solution {
public:
    int getLastMoment(int n, vector<int>& left, vector<int>& right) {
        int left_max = 0, right_min = n;
        for(int i = 0; i < left.size(); i++)
            left_max = max(left_max, left[i]);
        for(int i = 0; i < right.size(); i++)
            right_min = min(right_min, right[i]);
        int res = 0;
        res = max(res, (int)max(left_max, n - right_min));
        return res;
    }
};

3.LeetCode 1504. 统计全 1 子矩形

题目描述

给你一个只包含 0 和 1 的 rows * columns 矩阵 mat ,请你返回有多少个 子矩形 的元素全部都是 1 。

示例 1:
输入:mat = [[1,0,1],
[1,1,0],
[1,1,0]]
输出:13
解释:
有 6 个 1x1 的矩形。
有 2 个 1x2 的矩形。
有 3 个 2x1 的矩形。
有 1 个 2x2 的矩形。
有 1 个 3x1 的矩形。
矩形数目总共 = 6 + 2 + 3 + 1 + 1 = 13 。
示例 2:
输入:mat = [[0,1,1,0],
[0,1,1,1],
[1,1,1,0]]
输出:24
解释:
有 8 个 1x1 的子矩形。
有 5 个 1x2 的子矩形。
有 2 个 1x3 的子矩形。
有 4 个 2x1 的子矩形。
有 2 个 2x2 的子矩形。
有 2 个 3x1 的子矩形。
有 1 个 3x2 的子矩形。
矩形数目总共 = 8 + 5 + 2 + 4 + 2 + 2 + 1 = 24 。
示例 3:
输入:mat = [[1,1,1,1,1,1]]
输出:21
示例 4:
输入:mat = [[1,0,1],[0,1,0],[1,0,1]]
输出:5

提示:

  • 1 <= rows <= 150
  • 1 <= columns <= 150
  • 0 <= mat[i][j] <= 1

分析

  • 算法: 动态规划
  • 时间复杂度: o(n3)
  • 基本思路:
  1. 保存每个点左边到它连续个点的数量
  2. 固定矩形右下点,然后遍历从该层到最上面一层,统计能够构成矩形的左上点的个数(按最下行枚举,每行左上角点的宽度不能超过所有行的最小值

代码

class Solution {
public:
    int numSubmat(vector<vector<int>>& mat) {
        int n = mat.size();
        if(n == 0)return 0;
        int m = mat[0].size();
        int res = 0;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                if(mat[i][j] && j)
                    mat[i][j] += mat[i][j - 1];
        
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
                if(mat[i][j])
                    for(int minm = 151, k = i; k >= 0; k--)
                    {
                        minm = min(minm, mat[k][j]);
                        res += minm;
                    }
        return res;
    }
};

4.LeetCode 1505. 最多 K 次交换相邻数位后得到的最小整数

题目描述

给你一个字符串 num 和一个整数 k 。其中,num 表示一个很大的整数,字符串中的每个字符依次对应整数上的各个 数位 。
你可以交换这个整数相邻数位的数字 最多 k 次。
请你返回你能得到的最小整数,并以字符串形式返回。

示例 1:
示例1
输入:num = “4321”, k = 4
输出:“1342”
解释:4321 通过 4 次交换相邻数位得到最小整数的步骤如上图所示。
示例 2:
输入:num = “100”, k = 1
输出:“010”
解释:输出可以包含前导 0 ,但输入保证不会有前导 0 。
示例 3:
输入:num = “36789”, k = 1000
输出:“36789”
解释:不需要做任何交换。
示例 4:
输入:num = “22”, k = 22
输出:“22”
示例 5:
输入:num = “9438957234785635408”, k = 23
输出:“0345989723478563548”

提示:
1 <= num.length <= 30000
num 只包含 数字 且不含有 前导 0 。
1 <= k <= 10^9

分析

  • 算法: 贪心 + 树状数组
  • 时间复杂度: o(nlogn)
  • 基本思路:
  1. 贪心:每次使得前面的数尽可能小
  2. 遍历一遍num,每次在后面寻找距离当前位置小于等于k的最小数与当前数进行交换,更新k
  3. 因为每次交换除了从后面移到前面的那个数x,其余数的相对位置均保持不变,所以可以用一个队列数组q[0-9]存储0-9的在num中出现的所有位置(x交换后会被弹出)
  4. 设元素到遍历元素位置的距离为dist,每次从小到大遍历q[d = (0-9)],若队首元素的dist小于等于k,则用当前d更新当前元素
  5. 由于每个元素的dist等于初始位置pos-其前面已经使用的元素个数cnt,可以利用树状数组来弄,若当前元素被使用,则更新当前位置数状数组的值为1,没用过则为0,前缀和即为cnt
  6. 树状数组下标从1开始
  7. 在队列处理完毕后,原字符串不再提供信息,可直接对其修改

代码

class Solution {
public:
    vector<int>tr;
    int lowbit(int x)
    {
        return x & -x;
    }
    void update(int x, int n, int delta)
    {
        for(int i = x; i <= n; i += lowbit(i))
            tr[i] += delta;
    }
    int query(int x)
    {
        int res = 0;
        for(int i = x; i; i -= lowbit(i))
            res += tr[i];
        return res;
    }
    string minInteger(string num, int k) {
        int n = num.size();
        tr = vector<int>(n + 1, 0);
        queue<int>q[10];
        for(int i = 0; i < n; i++)q[num[i] - '0'].push(i);
        for(int i = 0; i < n; i++)
            for(int j = 0; j <= 9; j++)
            {
                if(q[j].empty())continue;
                int pos = q[j].front();
                int cnt = query(pos + 1);
                if(pos - cnt <= k)
                {
                    k -= pos - cnt;
                    num[i] = j + '0';
                    update(pos + 1, n, 1);
                    q[j].pop();
                    break;
                }
            }
        return num;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值