LeetCode 第200场周赛 题解

挺简单的

文章目录

  • a.统计好三元组
    • a.题目
    • a.分析
    • a.参考代码
  • b.找出数组游戏的赢家
    • b.题目
    • b.分析
    • b.参考代码
  • c.排布二进制网格的最少交换次数
    • c.题目
    • c.分析
    • c.参考代码
  • d.最大得分
    • d.题目
    • d.分析
    • d.参考代码

a.统计好三元组

a.题目

给你一个整数数组 arr ,以及 abc 三个整数。请你统计其中好三元组的数量。
如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。

  • 0 <= i < j < k < arr.length
  • |arr[i] - arr[j]| <= a
  • |arr[j] - arr[k]| <= b
  • |arr[i] - arr[k]| <= c

其中 |x| 表示 x 的绝对值。
返回 好三元组的数量 。

示例 1

输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。

示例 2

输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。

提示

  • 3 <= arr.length <= 100
  • 0 <= arr[i] <= 1000
  • 0 <= a, b, c <= 1000

a.分析

直接三个for模拟就完事了 没怎么想
大概可以想一下这三个条件存在什么性质 只枚举j和k

暴力O(n^3)能过就完了

a.参考代码

class Solution {
public:
    int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
        int ans=0;
        int n=arr.size();
        for(int i=0;i<n-2;i++)
            for(int j=i+1;j<n-1;j++)
                for(int k=j+1;k<n;k++)
                    if(abs(arr[i]-arr[j])<=a&&abs(arr[j]-arr[k])<=b&&abs(arr[i]-arr[k])<=c)ans++;
        return ans;
    }
};

b.找出数组游戏的赢家

b.题目

给你一个由 不同 整数组成的整数数组 arr 和一个整数 k 。
每回合游戏都在数组的前两个元素(即 arr[0] 和 arr[1] )之间进行。比较 arr[0] 与 arr[1] 的大小,较大的整数将会取得这一回合的胜利并保留在位置 0 ,较小的整数移至数组的末尾。当一个整数赢得 k 个连续回合时,游戏结束,该整数就是比赛的 赢家
返回赢得比赛的整数。
题目数据 保证 游戏存在赢家。

示例 1

输入:arr = [2,1,3,5,4,6,7], k = 2
输出:5
解释:一起看一下本场游戏每回合的情况:
LeetCode 第200场周赛 题解_第1张图片
因此将进行 4 回合比赛,其中 5 是赢家,因为它连胜 2 回合。

示例 2

输入:arr = [3,2,1], k = 10
输出:3
解释:3 将会在前 10 个回合中连续获胜。

示例 3

输入:arr = [1,9,8,2,3,7,6,4,5], k = 7
输出:9

示例 4

输入:arr = [1,11,22,33,44,55,66,77,88,99], k = 1000000000
输出:99

提示

  • 2 <= arr.length <= 10^5
  • 1 <= arr[i] <= 10^6
  • arr 所含的整数 各不相同 。
  • 1 <= k <= 10^9

b.分析

首先我看到了1e9的k 就感觉是不是要用公式来做
1e5的规模大概率要用线性的
后来分析了下得出直接模拟就能做了
因为 当k>n的时候 赢的那个数肯定是最大的那个数
简单证明下 因为如果是其他数 他必定会遇到最大那个数 因为其他数都放在了后面

那么模拟就很简单了 用deque双端队列就能O(1)操作两端的 当然手写个链表也行

代码写了点注释

总的复杂度是O(n)的

b.参考代码

class Solution {
public:
    int getWinner(vector<int>& arr, int k) {
        if(k>=arr.size())return *max_element(arr.begin(),arr.end());	//直接返回
        deque<int> dq;	//双端队列模拟
        for(auto i:arr)dq.push_back(i);
        int win=0,a,b;
        while(win<k){	//这个while最多会2k的复杂度
            a=dq.front();	//第一个
            dq.pop_front();
            b=dq.front();	//第二个
            if(a>b){	//第一个赢
                dq.pop_front();
                dq.push_front(a);
                dq.push_back(b);
                win++;
            }
            else{	
                dq.push_back(a);	//其实小的没有必要放在后面 因为必赢
                win=1;
            }
        }
        return max(a,b);	//有可能第一次就第二个赢
    }
};

c.排布二进制网格的最少交换次数

c.题目

给你一个 n x n 的二进制网格 grid,每一次操作中,你可以选择网格的 相邻两行 进行交换。
一个符合要求的网格需要满足主对角线以上的格子全部都是 0 。
请你返回使网格满足要求的最少操作次数,如果无法使网格符合要求,请你返回 -1 。
主对角线指的是从 (1, 1) 到 (n, n) 的这些格子。

示例 1

输入:grid = [[0,0,1],[1,1,0],[1,0,0]]
输出:3

示例 2

输入:grid = [[0,1,1,0],[0,1,1,0],[0,1,1,0],[0,1,1,0]]
输出:-1
解释:所有行都是一样的,交换相邻行无法使网格符合要求。

示例 3

输入:grid = [[1,0,0],[1,1,0],[1,1,1]]
输出:0

提示

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 200
  • grid[i][j] 要么是 0 要么是 1 。

c.分析

这种最少步数的 必须得先想清楚怎么才是最少的
首先要先想出目标位置是哪里:
后缀0从上到下依次是n-1~0 然后这个后缀0是可以多的
比如说4*4矩阵中后缀0从上到下可以是3310 不必要是3210

那么就要想到的就是一个贪心 如果是符合后缀0的长度的话 肯定是越近越好(交换最少) 所以就要根据目标距离长度是否符合 来进行贪心

然后还有一个要想是如何交换才是最少次数 我的想法就是
像冒泡排序一样 不断向上把最上面的位置固定
简单证明下就是 这样子交换每次交换都是关键交换 不存在交换来又交换回去的多余交换 这样子就能保证最上面的位置都是必要的

流程:

  • 先预处理出来后缀0的数量
  • 从上往下处理 把对应的后缀0数量的行下标记录下来
    • 如果已经有相同后缀0数量了 那么把这行等价为-1后缀0的行 因为之前的行更加再上面 距离目标位置更近
    • 不断处理直到等价位置没出现过
  • 处理完后 后缀0行下标必定全部都填好 如果没填到 就证明缺少一个长后缀 返回-1

复杂度为O(n^2) 因为模拟了交换时候的其他位置会被挤向下
这个其实可以用区间结构给优化掉
但是优化没有啥意义 因为遍历矩阵就已经要平方了

c.参考代码

class Solution {
public:
    int minSwaps(vector<vector<int>>& grid) {
        int n=grid.size();
        vector<int> idx(n,-1),zero(n,0);
        for(int row=0;row<n;row++)	//先记录下后缀0的个数
            for(int col=n-1;col>=1;col--)
                if(!grid[row][col])zero[row]++;
                else break;
        for(int row=0;row<n;row++)	//处理下idx
            if(idx[zero[row]]==-1)idx[zero[row]]=row;
            else{
                int j=zero[row];
                while(j&&idx[j]!=-1)j--;	//不断跳位置
                idx[j]=row;
            }
        int ans=0;
        int pos=0;
        for(int i=n-1;i>=1;i--)	//不可能
            if(idx[i]==-1)return -1;
        for(int i=n-1;i>=1;i--){	//算一下交换的距离
            ans+=idx[i]-pos;
            for(int j=0;j<n;j++)if(idx[j]<idx[i])idx[j]++;
            pos++;
        }
        return ans;
    }
};

d.最大得分

d.题目

你有两个 有序 且数组内元素互不相同的数组 nums1 和 nums2 。
一条 合法路径 定义如下:

  • 选择数组 nums1 或者 nums2 开始遍历(从下标 0 处开始)。
    从左到右遍历当前数组。
  • 如果你遇到了 nums1 和 nums2 中都存在的值,那么你可以切换路径到另一个数组对应数字处继续遍历(但在合法路径中重复数字只会被统计一次)。

得分定义为合法路径中不同数字的和。
请你返回所有可能合法路径中的最大得分。
由于答案可能很大,请你将它对 10^9 + 7 取余后返回。

示例 1

输入:nums1 = [2,4,5,8,10], nums2 = [4,6,8,9]
输出:30
解释:合法路径包括:
[2,4,5,8,10], [2,4,5,8,9], [2,4,6,8,9], [2,4,6,8,10],(从 nums1 开始遍历)
[4,6,8,9], [4,5,8,10], [4,5,8,9], [4,6,8,10] (从 nums2 开始遍历)
最大得分为上图中的绿色路径 [2,4,6,8,10] 。

示例 2

输入:nums1 = [1,3,5,7,9], nums2 = [3,5,100]
输出:109
解释:最大得分由路径 [1,3,5,100] 得到。

示例 3

输入:nums1 = [1,2,3,4,5], nums2 = [6,7,8,9,10]
输出:40
解释:nums1 和 nums2 之间无相同数字。
最大得分由路径 [6,7,8,9,10] 得到。

示例 4

输入:nums1 = [1,4,5,8,9,11,19], nums2 = [2,3,4,11,12]
输出:61

提示

  • 1 <= nums1.length <= 10^5
  • 1 <= nums2.length <= 10^5
  • 1 <= nums1[i], nums2[i] <= 10^7
  • nums1 和 nums2 都是严格递增的数组。

d.分析

这个只要求最大得分 不管路径如何 那么第一想法就是动态规划dp
比大小我原来还想着那个求余怎么比 后来发现数据最多是1e12 开个long long绝对够了
那么直接可以得出转移方程

如果两个数组不一样的话 那么就只能加上前面的
如果两个数组一样的 那么就都变成大的 这样子就能换路了

处理的时候要注意
dp需要根据两个数组的进度进行dp 因为相同数字时候会遇到用到另一个dp数组的
所以必须要两边同步进行
因为是严格单调增的 所以可以双指针同步进行
如果不是的话 就得用hashmap预处理出一样的位置在哪

总的时间复杂度是O(n)的

d.参考代码

class Solution {
public:
    int maxSum(vector<int>& nums1, vector<int>& nums2) {
        vector<unsigned long long> dp1(nums1.size(),0),dp2(nums2.size(),0);
        int i=0,j=0;
        bool flag;	//flag为true表示第一个数组动 否则第二个数组
        if(nums1[0]<=nums2[0])flag=true;	//小的先手
        else flag=false;
        dp1[0]=nums1[0];	//设置下初始值
        dp2[0]=nums2[0];	//当然是有第一个数就一样的特殊情况
        while(i<nums1.size()&&j<nums2.size())
        {
            if(flag){
                if(nums1[i]==nums2[j]){
                    dp1[i]=max((i>0?dp1[i-1]:0)+nums1[i],dp2[j]);
                    dp2[j]=max(dp1[i],dp2[j]);	//有点多余 为了处理特殊情况
                }
                else dp1[i]=(i>0?dp1[i-1]:0)+nums1[i];
            }
            else{
                if(nums1[i]==nums2[j]){
                    dp2[j]=max((j>0?dp2[j-1]:0)+nums2[j],dp1[i]);
                    dp1[i]=max(dp1[i],dp2[j]);	//有点多余 为了处理特殊情况
                }
                else dp2[j]=(j>0?dp2[j-1]:0)+nums2[j];
            }
            if(nums1[i]<=nums2[j]){	//双指针 小的动
                i++;
                flag=true;
            }
            else{
                j++;
                flag=false;
            }
        }
		//处理下剩下的
        while(++i<nums1.size())dp1[i]=dp1[i-1]+nums1[i];
        while(++j<nums2.size())dp2[j]=dp2[j-1]+nums2[j];
        return max(dp1.back(),dp2.back())%int(1e9+7);
    }
};

你可能感兴趣的:(leetcode,周赛,数据结构,算法,动态规划,leetcode)