leetcode316场周赛

这篇博客探讨了如何通过最少操作次数使数组达到目标状态,涉及奇偶性分析和排序策略。博主分享了解决数组相等最小开销问题的数学与前缀和方法,并提出遍历子数组寻找最大公因数等于K的子数组数量的解决方案。此外,还介绍了判断两个事件是否存在冲突的简单字符串操作方法。
摘要由CSDN通过智能技术生成

更好的阅读体验

由于今天晚了20min,结果本来能ak的题目,没法ak了。伤心…排名不是特别好看,实在因为手速太慢。

image-20221023122134437

题目四: 使数组相似的最少操作次数

能看到是第一个做的这个题,也算是一点小心机,如果不会做,就直接放弃提交了,保一下名次嘛。

首先分析一下题目的一个操作

nums[i] += 2, nums[j] -= 2, 要求i != j。那么这就可以看成对两个数组分别操作,也就是:

  • nums[i] += 2
  • target[j] += 2
  • i != j

这样每一次操作,就是对两个数组中的不同下标数字分别加上2。题目说一定满足可以把nums变成target,就会满足下面的性质

  1. nums中奇数个数和target中的奇数个数一样多,偶数也一样多。因为奇数 + 2不可能变成偶数,偶数 + 2也不可能变成奇数。
  2. target中比nums中大的数字差值的和等于target中比nums中小的数字差值的和。

有了上面的性质,可以考虑分成下面的步骤:

  1. 分成奇偶进行计算。也就是对应分成两组,numsj和targetj,numso和targeto。
  2. 只考虑大于的差值,或者只考虑小于的差值就行了,因为是相等的。这里只考虑target大于nums的部分。

如果要求数组操作次数最少。那么第一个直观的想法,就是nums[i]变成距离target中最近的,也就是排序后坐标对应的,也就是nums[i] 变成target[i]就行了。

  • 屎山代码
public long makeSimilar(int[] nums, int[] target) {
        // 1. 排序
    	Arrays.sort(nums);
        Arrays.sort(target);
    	// 2. 分成奇和偶
        List<Integer>numsj = new ArrayList<>();
        List<Integer>numso = new ArrayList<>();
        List<Integer>to = new ArrayList<>();
        List<Integer>tj = new ArrayList<>();
        for (int x : nums) {
            if (x % 2 == 0) numso.add(x);
            else numsj.add(x);
        }
        for (int x : target) {
            if (x % 2 == 0) to.add(x);
            else tj.add(x);
        }
        int n = numso.size();
    	// 3. 只考虑大于的部分,计算nums变成target的最小差值。
        long ans = 0;
        for (int i = 0; i < n; i++) {
            if (to.get(i) > numso.get(i)) ans += (to.get(i) - numso.get(i)) / 2;
        }
        n = numsj.size();
        for (int i = 0; i < n; i++) {
            if (tj.get(i) > numsj.get(i)) ans += (tj.get(i) - numsj.get(i)) / 2;
        }
        return ans;
    }

最后,本来想着使用指针进行奇和偶的划分,但是,转念一想,为啥要为难自己呢?就直接开了四个数组进行奇偶的划分了。时间 O ( l o g n ) O(logn) O(logn),空间 O ( n ) O(n) O(n),和排序的时间复杂度一致。空间是排序使用 + 自己的四个数组。

题目三:使数组相等的最小开销

数学 + 前缀和 + 二分

说实话不是特别擅长打数学公式,凑合看下吧。

下面是把数组都变成 x x x的最小开销。

s = ∑ i a b s ( x − n u m s [ i ] ) ∗ c o s t [ i ] s =\sum_iabs(x - nums[i]) * cost[i] s=iabs(xnums[i])cost[i]

  • 当x >= nums[i]的时候 = ∑ i c o s t [ i ] ∗ x − ∑ i n u m s [ i ] ∗ c o s t [ i ] \sum_icost[i] * x - \sum_inums[i]*cost[i] icost[i]xinums[i]cost[i]
  • 当x < nums[i]的时候:=$\sum_inums[i]*cost[i]-\sum_icost[i] * x $

如果我们把(nums[i], cost[i])按照nums[i]排序,那么就可以在 O ( l o g n ) O(logn) O(logn)的时间复杂度内找到大于x和小于x的坐标i。如果已经知道了 ∑ i c o s t [ i ] \sum_icost[i] icost[i] ∑ i n u m s [ i ] ∗ c o s t [ i ] \sum_inums[i]*cost[i] inums[i]cost[i]就可以在 O ( 1 ) O(1) O(1)的时间复杂度内使用上面的两个公式求出s,而上述的求和在排序之后,已经成了一个连续的部分,所以可以使用前缀和进行求解

最后看一眼数据范围1 < nums[i] < 1e6,那么可以通过遍历x的方式,因为x一定是 [ n u m s m i n v , n u m s m a x v ] [nums_{minv}, nums_{maxv}] [numsminv,numsmaxv]中的一个数字。

时间复杂度 O ( m a x ( n u m s m a x v , n ) ∗ l o g n ) O(max(nums_{maxv}, n) * logn) O(max(numsmaxv,n)logn),空间复杂度 O ( n ) O(n) O(n)

public long minCost(int[] nums, int[] cost) {
        List<int[]> arrs = new ArrayList<>();
        int n = cost.length;
        for (int i = 0; i < n; i++) {
            arrs.add(new int[] {nums[i], cost[i]});
        }
        // 按照nums进行排序
        arrs.sort((x, y)->Integer.compare(x[0], y[0]));
        Arrays.sort(nums);
    	// 求前缀和
        long[] sumb = new long[n + 1];
        long[] suma = new long[n + 1];
        for (int i = 0; i < n; i++) {
            int c = arrs.get(i)[1];
            suma[i + 1] = suma[i] + c;
            sumb[i + 1] = sumb[i] + (long)nums[i] * c;
        }
        long ans = Long.MAX_VALUE;
        for (int i = nums[0]; i <= nums[n - 1]; i++) {
            // 二分求解
            int l = 0, r = n;
            while (l < r) {
                int mid = l + r >> 1;
                if (nums[mid] <= i) l = mid + 1;
                else r = mid;
            }
            long a1 = suma[l];
            long a2 = suma[n] - suma[l];
            long b1 = sumb[l];               // 小于,xa1 - b1
            long b2 = sumb[n] - sumb[l];     // 大于,b2 - xa2
            ans = Math.min(ans, (long)i * a1 - b1 + b2 - (long)i * a2);
        }
        return ans;
    }

题目二: 最大公因数等于 K 的子数组数目

没想到简单的办法,但是数据很小,直接遍历所有子数组求符合条件就行了。

 int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    public int subarrayGCD(int[] nums, int k) {
        // System.out.println(gcd(8, 4));
        // System.out.println(gcd(8, 6));
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            int d = nums[i];
            for (int j = i; j < n; j++) {
                d = gcd(nums[j], d);
                if (d == k) ans++;
                if (d < k) break;       // 小于就不可能再大了
            }
        }
        return ans;
    }

题目一: 判断两个事件是否存在冲突

  • 直接字符串操作,java.date应该也可以判断,但是不知道怎么用,也不知道能不能用。
public static boolean haveConflict(String[] event1, String[] event2) {
        String[] e1bg = event1[0].split(":");
        String[] e1ed = event1[1].split(":");
        String[] e2bg = event2[0].split(":");
        String[] e2ed = event2[1].split(":");
        int e1st = Integer.valueOf(e1bg[0]) * 60 + Integer.valueOf(e1bg[1]);
        int e1sp = Integer.valueOf(e1ed[0]) * 60 + Integer.valueOf(e1ed[1]);
        int e2st = Integer.valueOf(e2bg[0]) * 60 + Integer.valueOf(e2bg[1]);
        int e2sp = Integer.valueOf(e2ed[0]) * 60 + Integer.valueOf(e2ed[1]);
        if (e1st >= e2st && e1st <= e2sp) return true;
        if (e2st >= e1st && e2st <= e1sp) return true;
        return false;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值