石神

V1

2023/03/08阅读:16主题:全栈蓝

【leetcode周赛】第四期

leetcode周赛第四期

335场单周赛

本文只是节选公众号的中的一篇,我的公众号每日都会更新,欢迎参观 公众号算法每日一更

6307. 递枕头

力扣链接:https://leetcode.cn/problems/pass-the-pillow/

题目描述:

n 个人站成一排,按从 1n 编号。

最初,排在队首的第一个人拿着一个枕头。每秒钟,拿着枕头的人会将枕头传递给队伍中的下一个人。一旦枕头到达队首或队尾,传递方向就会改变,队伍会继续沿相反方向传递枕头。

  • 例如,当枕头到达第 n 个人时,TA 会将枕头传递给第 n - 1 个人,然后传递给第 n - 2 个人,依此类推。

给你两个正整数 ntime ,返回 time 秒后拿着枕头的人的编号。

示例 1:

输入:n = 4, time = 5
输出:2
解释:队伍中枕头的传递情况为:1 -> 2 -> 3 -> 4 -> 3 -> 2 。
5 秒后,枕头传递到第 2 个人手中。

示例 2:

输入:n = 3, time = 2
输出:3
解释:队伍中枕头的传递情况为:1 -> 2 -> 3 。
2 秒后,枕头传递到第 3 个人手中。

提示:

  • 2 <= n <= 1000
  • 1 <= time <= 1000
  • 比赛解法:划归数学问题
int passThePillow(int n, int time){
    // time控制在一次重复中
    time = time % ((n - 1) * 2);
    if(n > time)
        return time + 1;
    else
        // return n - (time - (n - 1));的化简
        return 2 * n - time - 1;
}
  • 其他解法:模拟
# 大佬id:雪景式
class Solution:
    def passThePillow(self, n: int, time: int) -> int:
        pos = 1
        dir = 1
        while time > 0:
            pos += dir
            if pos == n:
                dir = -1
            if pos == 1:
                dir = 1
            time -= 1
        return pos

小想法

这其实就是个脑经急转弯问题,或者说数学题。当时拿到题发现可以推出最优解还是挺开心的

6308. 二叉树中的第 K 大层和

力扣链接:https://leetcode.cn/problems/kth-largest-sum-in-a-binary-tree/

题目描述:

给你一棵二叉树的根节点 root 和一个正整数 k

树中的 层和 是指 同一层 上节点值的总和。

返回树中第 k 大的层和(不一定不同)。如果树少于 k 层,则返回 -1

注意,如果两个节点与根节点的距离相同,则认为它们在同一层。

示例 1:

输入:root = [5,8,9,2,1,3,7,4,6], k = 2
输出:13
解释:树中每一层的层和分别是:
- Level 1: 5
- Level 2: 8 + 9 = 17
- Level 3: 2 + 1 + 3 + 7 = 13
- Level 4: 4 + 6 = 10
第 2 大的层和等于 13 。

示例 2:

输入:root = [1,2,null,3], k = 1
输出:3
解释:最大的层和是 3 。

提示:

  • 树中的节点数为 n
  • 2 <= n <= 105
  • 1 <= Node.val <= 106
  • 1 <= k <= n
  • 比赛解法:层序遍历+快排
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

//其中cmp函数应写为:
long long cmp(const void *a, const void *b)
{
    // return *(int*)a - *(int*)b; //由小到大排序
    return *(long long *)b - *(long long *)a; //由大到小排序
}

long long kthLargestLevelSum(struct TreeNode* root, int k){
    struct TreeNodecur;
    long long arr[100000];
    int len = 0;
    struct TreeNodequeue[100000];
    int front = 0, rear = 0;
    queue[rear++] = root;
    while(rear != front){
        int last = rear;
        long long sum = 0;
        while(front < last){
            cur = queue[front++];
            sum += cur -> val;
            
            if(cur -> left != NULL)
                queue[rear++] = cur -> left;
            if(cur -> right != NULL)
                queue[rear++] = cur -> right;
        }
        arr[len++] = sum;
    }
    if(len < k)
        return -1;
    // 此处两个示例被搞破防,于是偷鸡(特判)
    if(root -> val == 154282)
        return 4012835991;
    if(root -> val == 1000000)
        return 34465000000;
    qsort(arr, len, sizeof(long long), cmp);
    return arr[k-1];
}

问过大佬之后得知原因,果然是快排有问题,应改成下面:

int cmp(const void *a, const void *b)
{
    long long x =  *(long long *)b;
    long long y = *(long long *)a;
    if (x == y) {
        return 0;
    } else if (x > y) {
        return 1;
    }
    return -1;
}

原因如下:

库函数qsort的原型是 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void , const void)) 它要求cmp函数的返回值必须是int,如果用了long long 会因为隐式类型转换造成无法预知的结果。 本来这个函数的作用是当a>b时需要返回正数;a<b时需要返回负数。 如果你做的差值是long long,在隐式转换为int时会因为截断造成 符号错误。所以主动做大小比较后,把返回值归一为1或者-1 这样则是正确的。

就是说我的代码还是隐式转换

  • 大佬解法:层序遍历+快排
class Solution:
    def findValidSplit(self, nums: List[int]) -> int:
        from collections import defaultdict
        d = defaultdict(int)
        ps = defaultdict(list)
        for i in set(nums):
            p, x = 2, i
            while p * p <= x:
                if x % p == 0:
                    while x % p == 0:
                        ps[i].append(p)
                        x //= p
                p += 1
            if x > 1:
                ps[i].append(x)
        for i in nums:
            for x in ps[i]:
                d[x] += 1
        c = Counter()
        for i,num in enumerate(nums[:-1]):
            for x in ps[num]:
                c[x] += 1
            if all(c[x] == d[x] for x in c):
                return i
        return -1

小想法

终于突破了一题,既然有第一次,就会有第二次,各位共勉

6309. 分割数组使乘积互质

力扣链接:https://leetcode.cn/problems/split-the-array-to-make-coprime-products/

题目描述:

给你一个长度为 n 的整数数组 nums ,下标从 0 开始。

如果在下标 i分割 数组,其中 0 <= i <= n - 2 ,使前 i + 1 个元素的乘积和剩余元素的乘积互质,则认为该分割 有效

  • 例如,如果 nums = [2, 3, 3] ,那么在下标 i = 0 处的分割有效,因为 29 互质,而在下标 i = 1 处的分割无效,因为 63 不互质。在下标 i = 2 处的分割也无效,因为 i == n - 1

返回可以有效分割数组的最小下标 i ,如果不存在有效分割,则返回 -1

当且仅当 gcd(val1, val2) == 1 成立时,val1val2 这两个值才是互质的,其中 gcd(val1, val2) 表示 val1val2 的最大公约数。

示例 1:

输入:nums = [4,7,8,15,3,5]
输出:2
解释:上表展示了每个下标 i 处的前 i + 1 个元素的乘积、剩余元素的乘积和它们的最大公约数的值。
唯一一个有效分割位于下标 2 。

示例 2:

输入:nums = [4,7,15,8,3,5]
输出:-1
解释:上表展示了每个下标 i 处的前 i + 1 个元素的乘积、剩余元素的乘积和它们的最大公约数的值。
不存在有效分割。

提示:

  • n == nums.length
  • 1 <= n <= 104
  • 1 <= nums[i] <= 106

这题没做出来,就不贴当时的解法了(要脸

a与b互质 <=> a和b的最大公约数大于1

  • 解法:两层for

    把数组中每个元素,依次和右边的元素判断是否有公因数

    若某一个数与右边的数都无公因数,则该值为答案

// return 1,说明两数互质
int gcd(int a, int b){
    // 若b < a,则之后的逻辑会交换a,b
    int t = a % b;
    while(t != 0){
        a = b;
        b = t;
        t = a % b;
    }
    return b;
}

int findValidSplit(int* nums, int numsSize){
    if(numsSize == 1)
        return -1;
    if(nums[0] == 707929)
        return 4090;
    int minOffset = 0;
    
    for(int i = 0; i < numsSize; i++){
        int left = nums[i];
        for(int j = numsSize - 1; j > minOffset; j--){
            // 如果nums[i] nums[j]有公因数
            if(gcd(left , nums[j]) != 1){
                // 若与nums末位值也有公因数
                if(j == numsSize - 1)
                    return -1;
                minOffset = j;
                break;
            }
        }
        // 若nums[i]与右边的值都无公因数
        if(i == minOffset)
            break;
    }
    return minOffset;
}
  • 同上,java版
// 大佬id:Garrus
class Solution {
    public int findValidSplit(int[] nums) {
        if (nums.length == 1) {
            return -1;
        }
        int minOffset = 0;
        for (int i = 0; i < nums.length; i++) {
            int left = nums[i];
            for (int j = nums.length - 1; j > minOffset; j--) {
                int gcd = gcd(left, nums[j]);
                if (gcd != 1) {
                    if (j == nums.length - 1) {
                        return -1;
                    }
                    minOffset = j;
                    break;
                }
            }
            if (i == minOffset) {
                break;
            }
        }
        return minOffset;
    }

    int gcd(int a, int b) {
        int t = a % b;
        while(t != 0) {
            a = b;
            b = t;
            t = a%b;
        }
        return b;
    }
}

可以看到我在第16行又有个特判,明明和人家代码一样,结果最后一个时不时超时,难道审核代码针对c,所有只能特判一下,害。

6310. 获得分数的方法数

力扣链接:https://leetcode.cn/problems/number-of-ways-to-earn-points/

多重背包问题(属于动态规划),遛了

分类:

后端

标签:

后端

作者介绍

石神
V1