最大化未出现自然数问题 | 豆包MarsCode AI刷题

58 阅读4分钟

最大化未出现自然数问题 | 豆包MarsCode AI刷题

问题描述

小F手中有一个包含n个自然数的集合(集合中的数字可能会重复出现)。他可以对集合中的数字进行若干次操作,每次操作可以选择集合中的一个数字x,并将其替换为一个大于x的数字y。小F的目标是通过这些操作,使得集合中最大的未出现的自然数K尽可能大。你的任务是计算出这个最大的K值。


测试样例

样例1:

输入:n = 5, a = [5, 0, 0, 2, 2] 输出:4

样例2:

输入:n = 6, a = [1, 0, 3, 4, 4, 5] 输出:2

样例3:

输入:n = 4, a = [0, 1, 2, 3] 输出:4

样例4:

输入:n = 7, a = [7, 6, 5, 4, 3, 2, 1] 输出:0

样例5:

输入:n = 5, a = [0, 1, 2, 1, 3] 输出:5

示例解析

  1. 输入n = 5, a = [5, 0, 0, 2, 2]

    • 排序后:a = [0, 0, 2, 2, 5]
    • 从 0 开始逐步覆盖:0 -> 1 -> 2 -> 3 -> 4。返回 4
  2. 输入n = 6, a = [1, 0, 3, 4, 4, 5]

    • 排序后:a = [0, 1, 3, 4, 4, 5]
    • 从 0 开始逐步覆盖:0 -> 1 -> 2。返回 2
  3. 输入n = 4, a = [0, 1, 2, 3]

    • 排序后:a = [0, 1, 2, 3]
    • 从 0 开始逐步覆盖:0 -> 1 -> 2 -> 3 -> 4。返回 4

题解思路

这道题目的目标是通过一系列操作,找到一个最大值 K,使得 K 是集合中最大的未出现的自然数。每次操作可以选择集合中的一个数字 x,并将其替换为一个大于 x 的数字 y。换句话说,操作的本质是可以增加集合中的数字,但不允许减少任何数字。我们的目标是尽量让集合中的自然数覆盖从 0 到 K-1 的所有自然数,从而使得 K 最大。

我们可以采用一个贪心算法来解决这个问题,步骤如下:

  1. 排序:首先对数组进行排序。因为我们希望逐步填补从 0 到 K-1 的自然数,排序后的数组使我们能够从最小的数字开始处理,方便找到当前能覆盖的最小自然数。
  2. 遍历并替换:遍历排序后的数组,尝试用当前元素来覆盖一个“未出现的”自然数。如果当前元素小于等于目标自然数(即“未出现的自然数”),则我们可以将其用来覆盖这个数字,同时更新目标自然数。
  3. 终止条件:如果当前元素大于目标自然数,那么说明我们无法用当前的数字来覆盖目标自然数,算法结束。此时,目标自然数即为集合中最大的未出现的自然数。

详细步骤

  1. 排序:先对数组进行升序排序,方便我们从小到大地填充自然数。
  2. 贪心选择:遍历数组中的每一个数字,尝试将其替换成目标自然数。如果当前数字小于目标自然数,那么我们就可以认为目标自然数已经被覆盖,目标自然数加 1。
  3. 判断:如果当前数字大于目标自然数,那么我们无法用这个数字覆盖目标自然数,说明目标自然数就是当前数组中最大的未出现的自然数。

代码实现

 import java.util.*;
 ​
 public class Main {
     public static int solution(int n, int[] a) {
         // 对集合进行排序
         Arrays.sort(a);
         
         int target = 0; // 当前目标自然数,初始化为0
         
         // 遍历排序后的数组
         for (int i = 0; i < n; i++) {
             // 如果当前元素大于等于目标自然数,则我们可以将它替换为目标自然数
             if (a[i] >= target) {
                 target++; // 覆盖目标自然数,目标自然数加1
             }
         }
         
         // 最终的target即为集合中最大的未出现自然数
         return target;
     }
 ​
     public static void main(String[] args) {
         // 测试用例
         System.out.println(solution(5, new int[]{5, 0, 0, 2, 2}) == 4);
         System.out.println(solution(6, new int[]{1, 0, 3, 4, 4, 5}) == 2);
         System.out.println(solution(4, new int[]{0, 1, 2, 3}) == 4);
         System.out.println(solution(7, new int[]{7, 6, 5, 4, 3, 2, 1}) == 0);
         System.out.println(solution(5, new int[]{0, 1, 2, 1, 3}) == 5);
     }
 }

解释

  1. 排序:首先对数组进行排序,确保我们能从小到大地处理数字。例如,[5, 0, 0, 2, 2] 排序后为 [0, 0, 2, 2, 5],这样我们可以逐步覆盖从 0 开始的自然数。

  2. 贪心策略:我们用一个变量 target 来表示当前我们希望覆盖的自然数。初始时 target = 0。遍历排序后的数组,遇到的每个数字:

    • 如果当前数字大于等于 target,我们可以将其替换为 target,然后让 target 增加 1,表示我们成功覆盖了一个自然数。
    • 如果当前数字小于 target,我们跳过它,因为它无法帮助我们覆盖新的自然数。
  3. 返回值:当数组遍历完成后,target 就是当前未出现的最大自然数。

总结

这道题通过贪心算法,从最小的自然数开始尝试逐步覆盖。排序使得我们可以有效地从小到大地填补自然数,最终求得最大的未出现自然数。