LeetCode周赛记录--第200场周赛

2020年8月2日,LeetCode第200场周赛个人记录。本次周赛题目相对而言有一点简单,至少可以全部做出来,但是做题速度上依旧有很大的提升空间,4到题目共耗时1小时34分钟,错误提交3次。原本在结束前全部做完还沾沾自喜,没想到一看排名只有300名,果然算法之路永无止境。
因为LeetCode并没有放出原题,所以暂时不贴出题目链接,等过几天原题放出后再贴。

题目1.统计好三元组

给你一个整数数组 arr ,以及 a、b 、c 三个整数。请你统计其中好三元组的数量。
如果三元组 (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 的绝对值,返回 好三元组的数量 。
3 <= arr.length <= 100
0 <= arr[i] <= 1000
0 <= a, b, c <= 1000

作为第一题送分题,本题还是十分简单的,因为数据规模不大,最大只有100个,所以考虑暴力法是可行的,直接上代码。

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

题目2.找出数组游戏的赢家

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

乍一看此题难度很大,因为k的数据可以达到10⁹,这意味着我们要进行这么多次的比较,但是仔细想一想,因为要保证一个数赢k次,而当k大于等于数组长度时,就意味着整个数组里最大的元素一定会被比较,并且中断前面所有数的连胜。即当k大于等于数组长度时,可以直接返回数组中最大值。这样我们要进行的比较规模大幅度减小为10⁵,如此小的规模,可以直接暴力解决。

class Solution {
public:
    int getWinner(vector<int>& arr, int k) {
        int size = arr.size();
        if (k > size - 1)       //直接返回数组中最大的元素
        {
            int maxVal = 0;
            for (auto x: arr)
                maxVal = max(maxVal, x);
            return maxVal;
        }
        
        int times = 0;
        for (int i = 1; i < size; ++i)      //暴力比较
        {
            if (arr[0] > arr[i])            
                ++times;
            else                            
            {
                int temp = arr[0];
                arr[0] = arr[i];
                arr[i] = temp;
                times = 1;
            }
            if (times == k)
                return arr[0];
        }
        /*
        如果遍历完一遍后任然没有元素连胜达到k次,那么此时数组中最大的元素一定已经被遍历过进行比较并且在首位,
        我们也知道最大的元素最后一定能连胜k次,所以直接返回arr[0]
        */
        return arr[0];                  
    }
};

题目3.排布二进制网格的最少交换次数

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

暴力,继续暴力,果然暴力解万法,说起来本次周赛所有的题目都是用暴力法解决,难道paypal就喜欢暴力?这道题目看要我们求最小操作次数,很多人可能和我一样想用动规或者贪心解决,但是实际上如果我们画几个矩阵操作一遍,就会发现只要能满足要求,那么不管怎么操作,操作次数都是一样的。所以我们直接一排一排的排好,交换,最后得出的交换次数就是最少的操作次数。每一排我们需要倒着计算连续的0的个数,下标为i的行需要的连续0的个数为size - 1 - i。

class Solution {
public:
    int minSwaps(vector<vector<int>>& grid) {
        int size = grid.size();
        int sum = 0;
        for (int i = 0; i < size - 1; ++i)
        {
            int j;
            for (j = i; j < size; ++j)
                 if (isValid(grid[j], size - 1 - i))       //isValid是用来判断第j行是否有连续的size - 1 - i个0来交换到第i行
                     break;
            if (j == size)          //找不到能换到第i行的那一行,所以返回-1
                return -1;
            for (; j > i; --j)      //交换相邻的两行
            {
                swap(grid[j], grid[j - 1]);
                ++sum;
            }
        }
        return sum;
    }
    
    bool isValid(vector<int> &a, int target)        
    {
        for (int i = a.size() - 1; i >= 0; --i)     //倒序计算连续的0的个数
        {
            if (a[i] == 1)
                break;
            --target;
        }
        return target <= 0;
    }
};

题目4.最大得分

你有两个 有序 且数组内元素互不相同的数组 nums1 和 nums2 。
一条 合法路径 定义如下:
选择数组 nums1 或者 nums2 开始遍历(从下标 0 处开始)。
从左到右遍历当前数组。
如果你遇到了 nums1 和 nums2 中都存在的值,那么你可以切换路径到另一个数组对应数字处继续遍历(但在合法路径中重复数字只会被统计一次)。
得分定义为合法路径中不同数字的和。
请你返回所有可能合法路径中的最大得分。
由于答案可能很大,请你将它对 10^9 + 7 取余后返回。
1 <= nums1.length <= 10^5
1 <= nums2.length <= 10^5
1 <= nums1[i], nums2[i] <= 10^7
nums1 和 nums2 都是严格递增的数组。

让我们把暴力贯彻到底,本题因为两个数组都是递增的数组,所以不会出现跳来跳去成环的情况。我们把都存在的值称作分叉点,我们要关注的其实是两个分叉点之间走nums1还是走nums2,这个很好办,我们都走一遍算一下那个值更大,我们就走哪一条路。具体思路是,先遍历两个数组,用map存储每个值出现的次数,我们可以把开头和结尾也视为分叉点,每当我们遇到分叉点时,我们将分叉点的值加上去,然后分别计算nums1和nums2到下一个分叉点的和,将大的数加到总和上,这样我们就遇到了下一个分叉点,重复此过程,就能得到最终结果。要注意的是,计算和的时候数字可能会很大超过int可表示范围,我们使用long甚至是long long来计算和。

class Solution {
public:
    int maxSum(vector<int>& nums1, vector<int>& nums2) {
        long long sum = 0;          //防止整形溢出
        const int MOD = 1e9 + 7;       //取模的常数
        map<int, int> myMap;
        for (auto x: nums1)     //两个for遍历数组计算每个值出现的次数
            myMap[x]++;
        for (auto x: nums2)
        {
            myMap[x]++;
            if (myMap[x] == 2)          //将出现两次的数提前加到sum中,方便统一处理
            {
                sum += x;
                sum %= MOD;
            }
        }
        long long sum1 = 0, sum2 = 0;
        int index1 = 0, index2 = 0;
        while (index1 < nums1.size() || index2 < nums2.size())  //当两个数组都遍历到尾时停止
        {
            sum1 = sum2 = 0;
            for (; index1 < nums1.size() && myMap[nums1[index1]] != 2; ++index1)    //计算nums1到下一个分叉点的和
                sum1 += nums1[index1];
            for (; index2 < nums2.size() && myMap[nums2[index2]] != 2; ++index2)    //计算nums2到下一个分叉点的和
                sum2 += nums2[index2];
            sum += max(sum1, sum2);     
            sum %= MOD;
            ++index1;   //此时index1和index2在分叉点的位置上,为了方便统一处理,我们直接跳过分叉点
            ++index2;
        }
        return sum;
    }
};

本次周赛虽然比较简单,而且题目都是用暴力法解出来,但是也是我第一次在周赛中完整的做出4道题目,虽然排名不怎么样,但也是一个不小的进步。创作不易,还请多多支持。

你可能感兴趣的:(LeetCode周赛记录,c++,leetcode,算法,数据结构)