LeetCode-第334场周赛题解

第一题-6369. 左右元素和的差值

6369. 左右元素和的差值

类似题目:剑指 Offer 66. 构建乘积数组

【前缀和+后缀和思想 & 递推】

  • 方法1:
    • 求出元素的前缀和、后缀和(都不包含该元素),二者相减取绝对值即为答案
    • 两次遍历:前缀和一次正向遍历(存放的数组作为答案数组),后缀和一次逆向遍历(变量tmp动态记录当前后缀和的值,tmp += b[i+1];)并同步计算答案(b[i] = abs(b[i] - tmp);
    • 复杂度
      • 时间复杂度: O ( n ) O(n) O(n)
      • 空间复杂度: O ( 1 ) O(1) O(1)(答案数组不算)
    vector<int> leftRigthDifference(vector<int>& nums) {
        vector<int> b(nums.size(), 0);
        for(int i = 1; i< nums.size(); i++) b[i] = b[i-1] + nums[i-1];
        int tmp = 0;
        for(int i = nums.size()-2; i>= 0; i--){
            tmp += nums[i+1];
            b[i] = abs(b[i] - tmp);
        }
        return b;
    }
  • 方法2:思维,推导
    • 可得:
      • leftSum[i] = leftSum[i-1] + nums[i-1];
      • rightSum[i] = rightSum[i-1] - nums[i];
    • 所以:
      • 两个变量left,right分别动态维护leftSum[i]和rightSum[i]即可
      • 初始:left(0) = 0,right(0) = sum{nums[1~(n-1)]}
        • 也可以只开一个变量动态维护leftSum[i]-rightSum[i]
    vector<int> leftRigthDifference(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n, 0);
        int left = 0, right = 0;
        for(int i = 1; i < n; i++) right += nums[i];
        res[0] = abs(left - right);
        for(int i = 1; i< n; i++){
            left += nums[i-1];
            right -= nums[i];
            res[i] = abs(left - right);
        }
        return res;
    }

第二题-6368. 找出字符串的可整除数组

6368. 找出字符串的可整除数组

【数学,递推(模运算的性质)】

题干:前缀word[0,...i]组成的数nums(i)能否整除m,即:nums(i)对m取模是否为0。

  • 该题是判断所有的前缀word[0,...i]是否满足条件,考虑word[0,...i]word[0,...(i-1)]的联系
  • 对于word[0,...(i-1)]所组成的数nums(i-1)word[0,...i]组成的数nums(i) = nums(i-1)*10 + int(word[i])
    • 而要求的是nums(i)对m取模是否为0,不妨设nums(i)模m的余数为mod(i),则:
      • mod(i) = nums(i)%m
        = (nums(i-1) * 10 + int(word[i])) %m
        = (nums(i-1) * 10 %m) + int(word[i]) %m
        = (nums(i-1) %m) * (10 %m) + int(word[i]) %m
        = mod(i-1) * 10 %m + int(word[i])%m (由 mod(i) = nums(i)%m num(i-1) %m = mod(i-1)
        = (mod(i-1)*10 + int(word[i])) %m
      • 利用的是模运算的性质:
        - (a+b)%m = a%m + b%m
        - (a*b)%m = a%m * b%m
      • 所以mod(i) = (mod(i-1)*10 + int(word[i])) %m
        • 根据这个递推式,可直接正向遍历计算出当前mod得到答案
  • 复杂度
    • 时间复杂度: O ( n ) O(n) O(n)
    • 空间复杂度: O ( 1 ) O(1) O(1)
    vector<int> divisibilityArray(string word, int m) {
        int n = word.size();
        vector<int> div(n,0);
        long long mod = 0;
        for(int i = 0; i< word.size(); i++){
            mod = (10ll*mod + (word[i] - '0')) % m;
            div[i] = (!mod);
        }
        return div;
    }

第三题-6367. 求出最多标记下标

6367. 求出最多标记下标

【贪心+双指针 & 二分】

题干:数组nums中满足 2 * nums[i] <= nums[j](且i!=j)的元素可以配对,配对后就不能再和其它元素配对了,求出nums中最多可以配对的对数(该题答案是对数*2)。

  • 方法1:贪心,双指针
    • 将数组升序排序,对于前面的元素nums[i],应该尽可能配对>=2*nums[i]且最小的元素,这样能保证在nums[i]后面比它大的元素也尽可能被配对
      • 但这样会导致这样一种情况:
        • [3,6,8,12],3配对了6,但8无法配对12;应当是3配对8,6配对12
        • 原因是3配对了(能配对别的元素的)6,导致6无法再配对别的元素
      • 所以,对于一个要主动匹配别的元素的元素nums[i],它的候选配对列表里的元素,要满足:不可能作为主动方,去匹配别的元素
        • 很显然,n个元素,至多有n/2对配对,排序后数组的前半段元素都可能作为主动方匹配别的元素
    • 如何计数匹配对数:
      • 双指针i、j,i指向前半段元素开头,j指向后半段元素开头(n个元素,至多有n/2对配对,所以n为奇数时,显然右半段要更大)
        • 2*nums[i]<=nums[j]i++,j++,cnt++;
          • 否则:j++
          • 直到i==mid+1(主动方都匹配完了)或j==n(没有可匹配的了)为止
      • 答案就是cnt << 1
    • 复杂度:
      • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)(排序 O ( n l o g n ) O(nlogn) O(nlogn),双指针遍历 O ( n ) O(n) O(n)
      • 空间复杂度: O ( 1 ) O(1) O(1)
    int maxNumOfMarkedIndices(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int cnt = 0;
        int i = 0 ,j = ((n+1)>>1);
        int mid = j-1;
        while(i <= mid && j < n){
            if(2*nums[i] <= nums[j]){
                i++;
                j++;
                cnt++;
            }
            else j++;
        }
        return (cnt << 1);
    }
  • 方法2:二分
    • 将数组升序排序。对于有n个元素的数组,至多匹配n/2对
      • 设最大匹配对数为k
        • 显然:<k的对数,也能满足都匹配(为true)
          • >k的对数,不能满足都匹配(为false)
        • 以k作为分界点,满足单调性:<=k为1,>k为0
        • 所以可以二分答案转化为判定
      • 二分:
        • 设x为尝试匹配的对数,f(x)为该数组能否匹配x对
          • 随着x增大,f(x)在一个分界点从1变到0
            • 分界点即为所求
          • 即:求f(x) == 1的右边界
      • 初始化:x最小为0,最大为n/2,取值范围是[0,n/2],所以left = 0,right = n/2
    • 复杂度
      • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)(排序 O ( n l o g n ) O(nlogn) O(nlogn),二分 O ( n l o g n ) O(nlogn) O(nlogn)
      • 空间复杂度: O ( 1 ) O(1) O(1)
    int maxNumOfMarkedIndices(vector<int>& nums){
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int left = 0, right = (n>>1);
        int ans = 0;
        while(left <= right){
            int mid = left + ((right - left) >>1);
            if(check(nums, mid)){ //寻找check(mid) == 1的右边界
                ans = mid;
                left = mid+1;
            }
            else right = mid-1;
        }
        return (ans << 1);
    }
    bool check(vector<int>& nums, int k){
        for(int i = 0; i < k; i++){
            int j = (nums.size()-1+1-k)+i;
            if(2*nums[i] > nums[j]) return false;
        }
        return true;
    }

第四题-6366. 在网格图中访问一个格子的最少时间

6366. 在网格图中访问一个格子的最少时间

【最短路,Dijkstra算法】

题干:m*n的网格,0时刻从(0,0)出发,目的地是(m-1,n-1)。对于每个位置(x,y),都标明了该位置允许最早到达的时间grid[x][y](早于这个时间不让到达),要求每个时刻都要动不能原地踏步,问最早到达(m-1,n-1)的时间(不能到达(m-1,n-1)返回-1)。

  • 要求不能原地踏步,所以在t不满足>=grid[x][y]时,至少得有2个格子能够来回倒腾耗时间。所以0时刻从(0,0)出发后,必须得到达(0,1)或(1,0)这两个格子的一个
    • 只要能够到达(0,1)或(1,0),那么最终一定能到达(m-1,n-1)
    • 所以,若gird[0][1] > 1 && gird[1][0] > 1,意味着(m-1,n-1)不可达,直接返回-1
  • 对于m*n的矩阵可以抽象成一张图,从一个位置走到其相邻位置,花费的时间为1
    • 求最早到达(m-1,n-1)的时间,即求从(0,0)到(m-1,n-1)花费的最小时间
      • 这是个最短路问题(且无负权边),可用Dijkstra算法
      • 但是还有限制:到达(x,y)的时间不能早于grid[x][y]
        • 即从(0,0)到(x,y),花费的时间不能少于grid[x][y]
          • 就是说,从一个点(x,y)到相邻的点(x’,y’),必须满足:
            • 当前点(x,y)花费的时间+1>=相邻点(x’,y’)允许到达的最小花费时间grid[x'][y']
              • 若不满足,可以通过来回倒腾(每次+2),直到满足条件
              • 倒腾的次数为(grid[x'][y']+1 - (d[x][y]+1))/2
          • 所以使用Dijkstra算法,用点(x,y)更新点(x’, y’)=(x+dx[i], y+dy[i])的d[x'][y']时,需要先使上述的条件满足
            • 用来与d[x'][y']比较的t等于:
              • d[x][y]+1d[x][y]+1>=grid[x'][y'],不需要倒腾)
              • d[x][y]+1 + (grid[x'][y']+1 - (d[x][y]+1))/2 * 2d[x][y]+1<grid[x'][y'],需要倒腾)
            • 其实还可以转化成奇偶一致性判断
              • 因为每次倒腾时间都+2,与完全不倒腾花费时间的奇偶性是一致的
              • 偶数时间从(0,0)出发,所以对于点(x,y),若x+y为奇数,则它是奇数时间可达;若x+y为偶数,则它是偶数时间可达
                • 所以t = max(d[x][y]+1,grid[x'][y'])
                • grid[x'][y'],仅表明允许到达的最早时间(最小花费时间),不能保证这个时间就能到达
                  • 通过(grid[x'][y'] - (x+y))&1判断grid[x'][y'](x+y)的奇偶性是否一致来判断grid[x'][y']这个时间能否到达
                  • 不能达到(即不一致),dis要+1
  • 复杂度:
    • 时间复杂度: O ( ( m n ) l o g ( m n ) ) O((mn)log(mn)) O((mn)log(mn))
    • 空间复杂度: O ( m n ) O(mn) O(mn)
  • 细节:
    • 可达性判断
    • 条件满足判断(奇偶一致性判断)
    int minimumTime(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size(); //题目保证m,n>=2
        if(grid[0][1] >1 && grid[1][0] > 1) return -1;

        vector<vector<int>> d(m, vector<int>(n, INT_MAX));
        priority_queue<node> q;
        d[0][0] = 0;
        q.push(node{0,0,0});
        while(!q.empty()){
            node p = q.top();
            q.pop();
            if(p.t > d[p.x][p.y]) continue;

            for(int i = 0; i < 4; i++){
                int x = p.x + dx[i];
                int y = p.y + dy[i];
                if(x < 0 || y < 0 || x >=m || y >= n) continue;

                // int t = max(d[p.x][p.y]+1, grid[x][y]); //d[p.x][p.y]与p.t的值是一样的
                // t += ((t - (x+y))&1); //奇偶一致性判断
                int t = d[p.x][p.y]+1 + ((d[p.x][p.y] + 1 >= grid[x][y] )? 0 : (grid[x][y]+1 - (d[p.x][p.y]+1))/2*2);
                if(t < d[x][y]){ //是小于
                    d[x][y] = t;
                    q.push(node{x, y, t});
                }
            }
        }
        return d[m-1][n-1];
    }
    int dx[4] = {-1,1,0,0};
    int dy[4] = {0,0,-1,1};
    struct node{
        int x, y;
        int t;
        node(int x, int y, int t=0): x(x), y(y), t(t){}
        friend bool operator<(const node& a, const node& b){
            return a.t > b.t;
        }
    };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值