请写函数返回正数数组 arr 的最小不可组成和

问题描述

        给定一个正数数组 arr,其中所有的值都为整数,以下是最小不可组成和的概念:
        把 arr 每个子集内的所有元素加起来会出现很多值,其中最小的记为 min,最大的记为max 在区间[min,max]上,如果有数不可以被arr某一个子集相加得到,那么其中最小的那个数是arr 的最小不可组成和 在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最 小不可组成和。请写函数返回正数数组 arr 的最小不可组成和。
【举例】
        arr=[3,2,5]。子集{2}相加产生 2 为 min,子集{3,2,5}相加产生 10 为 max。在区间[2,10] 上,4、 6 和 9 不能被任何子集相加得到,其中 4 是 arr 的最小不可组成和。

        arr=[1,2,4]。子集{1}相加产生 1 为 min,子集{1,2,4}相加产生 7 为 max。在区间[1,7]上, 任何 数都可以被子集相加得到,所以 8 是 arr 的最小不可组成和。

思路

思路一: 使用暴力递归的方法进行解题。字符串arr每个下标i所对应的字符都有两种选择选与不选,因此我们可以计算出所有情况下的和,我们从最小的和开始遍历一次查找刚才结果中是否存在,最不存在直接返回结果,若遍历结束也没找到答案,则返回全部求和加一的结果。

思路二: 使用动态规划的思想解题。首先求出字符串中全部数据的求和记为sum,定义一个二维数组,行表示字符串arr的每一个字符,列表示0~sum。

        我们知道二维数组的第一列全部为true,第一行的其他位置dp[0][arr[0]]为true,第一行其他位置全部为false。

        二维数数组中间其他位置的判断标准为:dp[i][j] = dp[i-1][j] || ((j-arr[i]>=0)?dp[i-1][j-arr[i]]:false)

        从最小的开始遍历,查找表格中第一个为false的位置,返回纵坐标的值。若遍历完二维表全部为true,则返回sum+1。

思路三: 该思路使用前提是arr数组中必须有1。对数组中的数字进行排序,i表示数组的小标,定义变量range表示下标i之前子字符串的和。分为两种情况:1.当前的值( arr[i] )大于range+1,此时直接返回range+1。2.当前的值( arr[i] )小于等于range+1,range在原来的基础上加上当前的值。

代码

思路一代码

    public static int unformedSum1(int[] arr){
        if (arr==null||arr.length==0){
            return -1;
        }

        //收集所有min~max的能得到的累加和
        Set<Integer> set = new HashSet<>();
        process(arr,0,0,set);

        int min = Integer.MAX_VALUE;
        int sum = 0;
        for (int i =0;i<arr.length;i++){
            min = Math.min(min,arr[i]);
            sum += arr[i];
        }
        for (int i = min+1;i<=sum;i++){
            if (!set.contains(i)){
                return i;
            }
        }
        return sum+1;
    }

    private static void process(int[] arr,int index,int sum,Set<Integer> set){
        //如果此时已经没有元素 可选
        if (index==arr.length){
            if (!set.contains(sum)){
                set.add(sum);
            }
            return;
        }
        //要当前元素
        process(arr,index+1,sum+arr[index],set);
        //不要当前元素
        process(arr,index+1,sum,set);
    }

思路二代码

    public static int unformedSum2(int[] arr){
        if (arr==null||arr.length==0){
            return -1;
        }
        int sum = 0;
        int min = Integer.MAX_VALUE;
        for (int i =0;i<arr.length;i++){
            sum+=arr[i];
            min = Math.min(min,arr[i]);
        }
        Set<Integer> set = new HashSet<>();
        int cur = 0;
        boolean[][] dp = new boolean[arr.length][sum+1];
        for (int i =0;i<arr.length;i++){
            dp[i][0] = true;
        }
        dp[0][arr[0]] = true;
        for (int i =1;i<arr.length;i++){
            for (int j =1;j<=sum;j++){
                dp[i][j] = dp[i-1][j] ||((j-arr[i]>=0)?dp[i-1][j-arr[i]]:false);
            }
        }
        for (int j =min;j<=sum;j++){
            if (!dp[arr.length-1][j]){
                return j;
            }
        }
        return sum+1;
    }

思路三代码

    //已知arr中肯定有1这个数
    public static int unformedSum3(int[] arr){
        if (arr==null||arr.length==0){
            return 0;
        }
        Arrays.sort(arr);//O(N*logN)
        int range = 1;
        for (int i=1;i!=arr.length;i++){
            if (arr[i]>range+1){
                return range+1;
            }else {
                range += arr[i];
            }
        }
        return range+1;
    }

测试代码

    public static int[] generateArray(int len,int maxValue){
        int[] res = new int[len];
        for (int i =0;i<res.length-1;i++){
            res[i] = (int)(Math.random()*maxValue)+1;
        }
        res[res.length-1] = 1;
        return res;
    }

    public static int[] copyArray(int[] arr){
        if (arr==null||arr.length==0){
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0;i<arr.length;i++){
            res[i] = arr[i];
        }
        return res;
    }

    public static void printArray(int[] arr){
        for (int i=0;i!=arr.length;i++){
            System.out.print(arr[i]+" ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int len = 10;
        int maxValue = 30;
        int testTime = 100000;
        System.out.println("test begin!");
        for (int i =0;i<testTime;i++){
            int[] arr1 = generateArray(len,maxValue);
            int[] arr2 = copyArray(arr1);
            int[] arr3 = copyArray(arr1);
            int res1 = unformedSum1(arr1);
            int res2 = unformedSum2(arr2);
            int res3 = unformedSum3(arr3);
            if (res1 != res2 || res2 != res3){
                System.out.println("test Ops!");
                printArray(arr1);
                printArray(arr2);
                printArray(arr3);
                System.out.println(res1);
                System.out.println(res2);
                System.out.println(res3);
                break;
            }
        }
        System.out.println("test end!");
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值