1526.形成目标数组的子数组最少增加次数

目标

给你一个整数数组 target 和一个数组 initial ,initial 数组与 target 数组有同样的维度,且一开始全部为 0 。

请你返回从 initial 得到 target 的最少操作次数,每次操作需遵循以下规则:

  • 在 initial 中选择 任意 子数组,并将子数组中每个元素增加 1 。

答案保证在 32 位有符号整数以内。

示例 1:

输入:target = [1,2,3,2,1]
输出:3
解释:我们需要至少 3 次操作从 intial 数组得到 target 数组。
[0,0,0,0,0] 将下标为 0 到 4 的元素(包含二者)加 1 。
[1,1,1,1,1] 将下标为 1 到 3 的元素(包含二者)加 1 。
[1,2,2,2,1] 将下表为 2 的元素增加 1 。
[1,2,3,2,1] 得到了目标数组。

示例 2:

输入:target = [3,1,1,2]
输出:4
解释:(initial)[0,0,0,0] -> [1,1,1,1] -> [1,1,1,2] -> [2,1,1,2] -> [3,1,1,2] (target) 。

示例 3:

输入:target = [3,1,5,4,2]
输出:7
解释:(initial)[0,0,0,0,0] -> [1,1,1,1,1] -> [2,1,1,1,1] -> [3,1,1,1,1] 
                                  -> [3,1,2,2,2] -> [3,1,3,3,2] -> [3,1,4,4,2] -> [3,1,5,4,2] (target)。

示例 4:

输入:target = [1,1,1,1]
输出:1

说明:

  • 1 <= target.length <= 10^5
  • 1 <= target[i] <= 10^5

思路

有一个 target 数组和一个与其维度相同的的全 0 数组 initial,每次操作可以将 initial 任意子数组的每个元素加 1。求将 initial 变为 target 的最小操作次数。

将子数组全部加一很容易想到差分数组,按照贪心的思想,要将元素从 0 操作为 target[i] 一定需要 target[i] 次,问题在于有多少个元素可以共用这一个操作。

计算差分数组,仅统计其中大于 0 的部分,小于等于 0 说明可以公用前面的操作,大于 0 说明需要增加操作。

代码


/**
 * @date 2025-10-30 8:57
 */
public class MinNumberOperations1526 {

    public int minNumberOperations(int[] target) {
        int n = target.length;
        int res = target[0];
        for (int i = 1; i < n; i++) {
            int diff = target[i] - target[i - 1];
            if (diff > 0) {
                res += diff;
            }
        }
        return res;
    }
}

性能

3397.执行操作后不同元素的最大数量

目标

给你一个整数数组 nums 和一个整数 k。

你可以对数组中的每个元素 最多 执行 一次 以下操作:

  • 将一个在范围 [-k, k] 内的整数加到该元素上。

返回执行这些操作后,nums 中可能拥有的不同元素的 最大 数量。

示例 1:

输入: nums = [1,2,2,3,3,4], k = 2
输出: 6
解释:
对前四个元素执行操作,nums 变为 [-1, 0, 1, 2, 3, 4],可以获得 6 个不同的元素。

示例 2:

输入: nums = [4,4,4,4], k = 1
输出: 3
解释:
对 nums[0] 加 -1,以及对 nums[1] 加 1,nums 变为 [3, 5, 4, 4],可以获得 3 个不同的元素。

说明:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^9
  • 0 <= k <= 10^9

思路

有一个整数数组 nums,允许对数组中的每个元素加上 [-k, k] 的整数,求操作后数组中不同元素的最大个数。

先对数组进行排序,计算相同元素个数,同时记录前一个元素操作后的最大值 + 1,当前相同元素的个数可以操作变成不同元素的范围是 [Math.max(nums[start] - k, prev), Math.min(nums[start] + k, l + cnt - 1)]。

代码


/**
 * @date 2025-10-20 10:09
 */
public class MaxDistinctElements3397 {

    public int maxDistinctElements(int[] nums, int k) {
        int n = nums.length;
        int i = 0;
        int prev = Integer.MIN_VALUE;
        int res = 0;
        Arrays.sort(nums);
        while (i < n) {
            int start = i;
            while (i < n && nums[i] == nums[start]) {
                i++;
            }
            int cnt = i - start;
            int l = Math.max(nums[start] - k, prev);
            int r = Math.min(nums[start] + k, l + cnt - 1);
            res += r - l + 1;
            prev = r + 1;
        }
        return res;
    }

}

性能

2598.执行操作后的最大MEX

目标

给你一个下标从 0 开始的整数数组 nums 和一个整数 value 。

在一步操作中,你可以对 nums 中的任一元素加上或减去 value 。

  • 例如,如果 nums = [1,2,3] 且 value = 2 ,你可以选择 nums[0] 减去 value ,得到 nums = [-1,2,3] 。

数组的 MEX (minimum excluded) 是指其中数组中缺失的最小非负整数。

  • 例如,[-1,2,3] 的 MEX 是 0 ,而 [1,0,3] 的 MEX 是 2 。

返回在执行上述操作 任意次 后,nums 的最大 MEX 。

示例 1:

输入:nums = [1,-10,7,13,6,8], value = 5
输出:4
解释:执行下述操作可以得到这一结果:
- nums[1] 加上 value 两次,nums = [1,0,7,13,6,8]
- nums[2] 减去 value 一次,nums = [1,0,2,13,6,8]
- nums[3] 减去 value 两次,nums = [1,0,2,3,6,8]
nums 的 MEX 是 4 。可以证明 4 是可以取到的最大 MEX 。

示例 2:

输入:nums = [1,-10,7,13,6,8], value = 7
输出:2
解释:执行下述操作可以得到这一结果:
- nums[2] 减去 value 一次,nums = [1,-10,0,13,6,8]
nums 的 MEX 是 2 。可以证明 2 是可以取到的最大 MEX 。

说明:

  • 1 <= nums.length, value <= 10^5
  • -10^9 <= nums[i] <= 10^9

思路

有一个数组,可以执行 任意 次操作,每次操作可以将任意元素加上或者减去 value,求数组中缺失的最小非负整数的最大值。

题目要求返回缺失的最小非负整数,可以从 0 开始枚举每一个整数 target,问题是如何判断数组中是否存在元素 nums[i] + k * value == target。两边同时对 value 取余可得 nums[i] ≡ target (mod value)

可以对 nums[i] % value 计数,枚举 target 判断是否还剩有同余元素即可。

代码


/**
 * @date 2025-10-16 8:46
 */
public class FindSmallestInteger2598 {

    public int findSmallestInteger(int[] nums, int value) {
        int[] cnt = new int[value];
        for (int num : nums) {
            int rem = num % value;
            rem = rem >= 0 ? rem : rem + value;
            cnt[rem]++;
        }
        int res = 0;
        while (true) {
            int rem = res % value;
            if (cnt[rem] > 0) {
                cnt[rem]--;
            } else {
                return res;
            }
            res++;
        }
    }

}

性能

976.三角形的最大周长

目标

给定由一些正数(代表长度)组成的数组 nums ,返回 由其中三个长度组成的、面积不为零的三角形的最大周长 。如果不能形成任何面积不为零的三角形,返回 0。

示例 1:

输入:nums = [2,1,2]
输出:5
解释:你可以用三个边长组成一个三角形:1 2 2。

示例 2:

输入:nums = [1,2,1,10]
输出:0
解释:
你不能用边长 1,1,2 来组成三角形。
不能用边长 1,1,10 来构成三角形。
不能用边长 1、2 和 10 来构成三角形。
因为我们不能用任何三条边长来构成一个非零面积的三角形,所以我们返回 0。

说明:

  • 3 <= nums.length <= 10^4
  • 1 <= nums[i] <= 10^6

思路

已知正整数数组 nums,从中取出三个元素作为三角形的边长,求三角形的最大周长。

最大周长一定是长度最长的三条边,只需要判断能否组成三角形即可。如果不能,说明当前最大值无法组成三角形,将其移除后按同样的逻辑继续判断即可。

代码


/**
 * @date 2025-09-28 8:50
 */
public class LargestPerimeter976 {

    public int largestPerimeter(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        for (int i = n - 1; i >= 2; i--) {
            if (nums[i] < nums[i - 1] + nums[i - 2]) {
                return nums[i] + nums[i - 1] + nums[i - 2];
            }
        }
        return 0;
    }

}

性能

1733.需要教语言的最少人数

目标

在一个由 m 个用户组成的社交网络里,我们获取到一些用户之间的好友关系。两个用户之间可以相互沟通的条件是他们都掌握同一门语言。

给你一个整数 n ,数组 languages 和数组 friendships ,它们的含义如下:

  • 总共有 n 种语言,编号从 1 到 n 。
  • languages[i] 是第 i 位用户掌握的语言集合。
  • friendships[i] = [ui, vi] 表示 ui 和 vi 为好友关系。

你可以选择 一门 语言并教会一些用户,使得所有好友之间都可以相互沟通。请返回你 最少 需要教会多少名用户。

请注意,好友关系没有传递性,也就是说如果 x 和 y 是好友,且 y 和 z 是好友, x 和 z 不一定是好友。

示例 1:

输入:n = 2, languages = [[1],[2],[1,2]], friendships = [[1,2],[1,3],[2,3]]
输出:1
解释:你可以选择教用户 1 第二门语言,也可以选择教用户 2 第一门语言。

示例 2:

输入:n = 3, languages = [[2],[1,3],[1,2],[3]], friendships = [[1,4],[1,2],[3,4],[2,3]]
输出:2
解释:教用户 1 和用户 3 第三门语言,需要教 2 名用户。

说明:

  • 2 <= n <= 500
  • languages.length == m
  • 1 <= m <= 500
  • 1 <= languages[i].length <= n
  • 1 <= languages[i][j] <= n
  • 1 <= u​​​​​​i < v​​​​​​i <= languages.length
  • 1 <= friendships.length <= 500
  • 所有的好友关系 (u​​​​​i, v​​​​​​i) 都是唯一的。
  • languages[i] 中包含的值互不相同。

思路

n 种语言,编号 1 ~ n,同时有 m 个用户,编号从 1 ~ mlanguages[i] 表示编号为 i + 1 的用户所掌握的语言,friendships 数组记录了用户的朋友关系。现在可以选择 一门 语言教会任意用户使得所有朋友都可以沟通,求需要教的最少人数。

找出无法沟通的朋友关系(统计总人数 total),统计每一种语言的人数 cnt[i](注意去重),最少人数即 total - max(cnt)

代码


/**
 * @date 2025-09-10 8:49
 */
public class MinimumTeachings1733 {

    public int minimumTeachings(int n, int[][] languages, int[][] friendships) {
        int m = languages.length;
        int[] cnt = new int[n + 1];
        HashSet<Integer>[] lang = new HashSet[m + 1];
        Arrays.setAll(lang, x -> new HashSet<>());
        for (int i = 0; i < m; i++) {
            for (int language : languages[i]) {
                lang[i + 1].add(language);
            }
        }
        Set<Integer> set = new HashSet<>();
        for (int[] friendship : friendships) {
            int a = friendship[0];
            int b = friendship[1];
            HashSet<Integer> tmp = new HashSet<>(lang[a]);
            tmp.retainAll(lang[b]);
            if (tmp.size() == 0) {
                if (!set.contains(a)) {
                    for (Integer t : lang[a]) {
                        cnt[t]++;
                    }
                    set.add(a);
                }
                if (!set.contains(b)) {
                    for (Integer t : lang[b]) {
                        cnt[t]++;
                    }
                    set.add(b);
                }
            }
        }
        return set.size() - Arrays.stream(cnt).max().orElse(0);
    }

}

性能

1792.最大平均通过率

目标

一所学校里有一些班级,每个班级里有一些学生,现在每个班都会进行一场期末考试。给你一个二维数组 classes ,其中 classes[i] = [passi, totali] ,表示你提前知道了第 i 个班级总共有 totali 个学生,其中只有 passi 个学生可以通过考试。

给你一个整数 extraStudents ,表示额外有 extraStudents 个聪明的学生,他们 一定 能通过任何班级的期末考。你需要给这 extraStudents 个学生每人都安排一个班级,使得 所有 班级的 平均 通过率 最大 。

一个班级的 通过率 等于这个班级通过考试的学生人数除以这个班级的总人数。平均通过率 是所有班级的通过率之和除以班级数目。

请你返回在安排这 extraStudents 个学生去对应班级后的 最大 平均通过率。与标准答案误差范围在 10^-5 以内的结果都会视为正确结果。

示例 1:

输入:classes = [[1,2],[3,5],[2,2]], extraStudents = 2
输出:0.78333
解释:你可以将额外的两个学生都安排到第一个班级,平均通过率为 (3/4 + 3/5 + 2/2) / 3 = 0.78333 。

示例 2:

输入:classes = [[2,4],[3,9],[4,5],[2,10]], extraStudents = 4
输出:0.53485

说明:

  • 1 <= classes.length <= 10^5
  • classes[i].length == 2
  • 1 <= passi <= totali <= 10^5
  • 1 <= extraStudents <= 10^5

思路

有一个二维数组 classesclasses[i][0] 表示班级 i 期末考试中通过考试的人数,classes[i][1] 表示班级 i 的总人数。有 extraStudents 个聪明学生一定可以通过期末考试,现在需要将这些学生分配到班级中去,使得班级通过率的平均值最大。返回最大平均通过率。

为了使平均值更大,可以优先将聪明学生安排到通过率提升最大的班级,使用优先队列。

代码


/**
 * @date 2025-09-01 21:21
 */
public class MaxAverageRatio1792 {
    public double maxAverageRatio(int[][] classes, int extraStudents) {
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> (int) (((b[1] - b[0]) * (long) a[1] * (a[1] + 1) - (a[1] - a[0]) * (long) b[1] * (b[1] + 1)) % 1000000007));
        for (int[] item : classes) {
            q.offer(item);
        }
        for (int i = 0; i < extraStudents; i++) {
            int[] peek = q.poll();
            peek[0]++;
            peek[1]++;
            q.offer(peek);
        }
        double res = 0.0;
        for (int[] item : classes) {
            res += (double) item[0] / item[1];
        }
        return res / classes.length;
    }

}

性能

2348.全0子数组的数目

目标

给你一个整数数组 nums ,返回全部为 0 的 子数组 数目。

子数组 是一个数组中一段连续非空元素组成的序列。

示例 1:

输入:nums = [1,3,0,0,2,0,0,4]
输出:6
解释:
子数组 [0] 出现了 4 次。
子数组 [0,0] 出现了 2 次。
不存在长度大于 2 的全 0 子数组,所以我们返回 6 。

示例 2:

输入:nums = [0,0,0,2,0,0]
输出:9
解释:
子数组 [0] 出现了 5 次。
子数组 [0,0] 出现了 3 次。
子数组 [0,0,0] 出现了 1 次。
不存在长度大于 3 的全 0 子数组,所以我们返回 9 。

示例 3:

输入:nums = [2,10,2019]
输出:0
解释:没有全 0 子数组,所以我们返回 0 。

说明:

  • 1 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9

思路

返回数组的全 0 子数组个数。

长度为 k 的子数组个数为 (1 + k) * k / 2。可以使用贪心策略,如果当前元素是 0 找到以它为起点的最长连续 0 数组,计算子数组个数。

代码


/**
 * @date 2025-08-19 8:54
 */
public class ZeroFilledSubarray2348 {

    public long zeroFilledSubarray(int[] nums) {
        long res = 0L;
        int n = nums.length;
        int i = 0;
        while (i < n){
            if (nums[i] != 0){
                i++;
                continue;
            }
            int start = i;
            while (i < n && nums[i] == 0){
                i++;
            }
            int cnt = i - start;
            res += (1L + cnt) * cnt / 2;
        }

        return res;
    }
}

性能

2561.重排水果

目标

你有两个果篮,每个果篮中有 n 个水果。给你两个下标从 0 开始的整数数组 basket1 和 basket2 ,用以表示两个果篮中每个水果的交换成本。你想要让两个果篮相等。为此,可以根据需要多次执行下述操作:

  • 选中两个下标 i 和 j ,并交换 basket1 中的第 i 个水果和 basket2 中的第 j 个水果。
  • 交换的成本是 min(basket1i,basket2j) 。

根据果篮中水果的成本进行排序,如果排序后结果完全相同,则认为两个果篮相等。

返回使两个果篮相等的最小交换成本,如果无法使两个果篮相等,则返回 -1 。

示例 1:

输入:basket1 = [4,2,2,2], basket2 = [1,4,1,2]
输出:1
解释:交换 basket1 中下标为 1 的水果和 basket2 中下标为 0 的水果,交换的成本为 1 。此时,basket1 = [4,1,2,2] 且 basket2 = [2,4,1,2] 。重排两个数组,发现二者相等。

示例 2:

输入:basket1 = [2,3,4,1], basket2 = [3,2,5,1]
输出:-1
解释:可以证明无法使两个果篮相等。

说明:

  • basket1.length == bakste2.length
  • 1 <= basket1.length <= 10^5
  • 1 <= basket1i,basket2i <= 10^9

思路

代码

性能

3487.删除后的最大子数组元素和

目标

给你一个整数数组 nums 。

你可以从数组 nums 中删除任意数量的元素,但不能将其变为 空 数组。执行删除操作后,选出 nums 中满足下述条件的一个子数组:

  • 子数组中的所有元素 互不相同 。
  • 最大化 子数组的元素和。

返回子数组的 最大元素和 。

子数组 是数组的一个连续、非空 的元素序列。

示例 1:

输入:nums = [1,2,3,4,5]
输出:15
解释:
不删除任何元素,选中整个数组得到最大元素和。

示例 2:

输入:nums = [1,1,0,1,1]
输出:1
解释:
删除元素 nums[0] == 1、nums[1] == 1、nums[2] == 0 和 nums[3] == 1 。选中整个数组 [1] 得到最大元素和。

示例 3:

输入:nums = [1,2,-1,-2,1,0,-1]
输出:3
解释:
删除元素 nums[2] == -1 和 nums[3] == -2 ,从 [1, 2, 1, 0, -1] 中选中子数组 [2, 1] 以获得最大元素和。

说明:

  • 1 <= nums.length <= 100
  • -100 <= nums[i] <= 100

思路

有一个数组 nums,允许删除其中任意元素得到一个 非空 数组,使得最终数组中 不包含重复元素 并且所有 元素和最大

为了使和最大,我们应该删掉所有的负数,同时对剩余元素去重累加求和。需要注意如果数组中全是负数,需要保留最大元素。

代码


/**
 * @date 2025-07-25 8:55
 */
public class MaxSum3487 {

    public int maxSum(int[] nums) {
        int sum = Arrays.stream(nums).distinct().filter(x -> x > 0).sum();
        if (sum == 0){
            OptionalInt max = Arrays.stream(nums).max();
            return max.isPresent() ? max.getAsInt() : sum;
        }
        return sum;
    }

}

性能

1717.删除子字符串的最大得分

目标

给你一个字符串 s 和两个整数 x 和 y 。你可以执行下面两种操作任意次。

  • 删除子字符串 "ab" 并得到 x 分。
    • 比方说,从 "cabxbae" 删除 ab ,得到 "cxbae" 。
  • 删除子字符串"ba" 并得到 y 分。
    • 比方说,从 "cabxbae" 删除 ba ,得到 "cabxe" 。

请返回对 s 字符串执行上面操作若干次能得到的最大得分。

示例 1:

输入:s = "cdbcbbaaabab", x = 4, y = 5
输出:19
解释:
- 删除 "cdbcbbaaabab" 中加粗的 "ba" ,得到 s = "cdbcbbaaab" ,加 5 分。
- 删除 "cdbcbbaaab" 中加粗的 "ab" ,得到 s = "cdbcbbaa" ,加 4 分。
- 删除 "cdbcbbaa" 中加粗的 "ba" ,得到 s = "cdbcba" ,加 5 分。
- 删除 "cdbcba" 中加粗的 "ba" ,得到 s = "cdbc" ,加 5 分。
总得分为 5 + 4 + 5 + 5 = 19 。

示例 2:

输入:s = "aabbaaxybbaabb", x = 5, y = 4
输出:20

说明:

  • 1 <= s.length <= 10^5
  • 1 <= x, y <= 10^4
  • s 只包含小写英文字母。

思路

贪心策略,优先处理分值高的字符串。

代码


/**
 * @date 2025-07-23 9:01
 */
public class MaximumGain1717 {

    public int maximumGain(String s, int x, int y) {
        char prev, cur;
        int max, min;
        if (x > y) {
            prev = 'a';
            cur = 'b';
            max = x;
            min = y;
        } else {
            prev = 'b';
            cur = 'a';
            max = y;
            min = x;
        }
        Deque<Character> q = new ArrayDeque<>();
        q.push('^');
        int n = s.length();
        int res = 0;
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (q.peek() == prev && c == cur) {
                q.pop();
                res += max;
            } else {
                q.push(c);
            }
        }
        q.removeLast();
        Deque<Character> p = new ArrayDeque<>();
        p.push('^');
        while (!q.isEmpty()) {
            if (q.peekLast() == prev && p.peek() == cur) {
                q.removeLast();
                p.pop();
                res += min;
            } else {
                p.push(q.removeLast());
            }
        }
        return res;
    }

}

性能