• 数组的最小不可组成和问题


    作者:Grey

    原文地址:数组的最小不可组成和问题

    题目说明#

    链接:https://www.nowcoder.com/questionTerminal/296c2c18037843a7b719cf4c9c0144e4
    来源:牛客网

    给定一个全是正数的数组arr,定义一下arr的最小不可组成和的概念:

    1,arr的所有非空子集中,把每个子集内的所有元素加起来会出现很多的值,其中最小的记为min,最大的记为max;

    2,在区间[min,max]上,如果有一些正数不可以被arr某一个子集相加得到,那么这些正数中最小的那个,就是arr的最小不可组成和;

    3,在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和;

    举例: arr = {3,2,5}

    arr的min为2,max为10,

    在区间[2,10]上,4是不能被任何一个子集相加得到的值中最小的,所以4是arr的最小不可组成和;

    arr = {3,2,4}

    arr的min为2,max为9,

    在区间[2,9]上,8是不能被任何一个子集相加得到的值中最小的,所以8是arr的最小不可组成和;

    arr = {3,1,2} arr的min为1,max为6,

    在区间[2,6]上,任何数都可以被某一个子集相加得到,所以7是arr的最小不可组成和;

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

    思路#

    首先我们设置两个变量,maxmin用于记录数组累加得到的最大值,和当数组不为空累加得到的最小值。那么在数组非空状态下,累加和一定在[min, max]区间内。我们设置

    boolean[][] dp = new boolean[arr.length][max + 1];
    

    其中dp[i][j]表示[0....i]范围内的元素任意累加,能否组成j这个累加和

    显然有

    // 0元素可以组成arr[0]这个累加和
    dp[0][arr[0]] = true;
    
    for (int i = 0; i < dp.length; i++) {
         // 0..i上一个元素都不用,可以组成0这个累加和
         dp[i][0] = true;
    }
    

    这样我们得到dp这个数组第一行和第一列的情况。

    然后我们可以推导普遍位置

    dp[i][j] = dp[i - 1][j] || (j - arr[i] >= 0 && dp[i - 1][j - arr[i]]);
    

    其含义为:

    [0...i]范围内,任意选择,能否组成j这个累加和,其实包括了两种情况:

    情况1:[0...i-1]范围内,任意选择,能否组成j这个累加和,如果可以,说明dp[i][j]=true

    情况2:[0...i-1]范围内,任意选择,能否组成j-arr[i]这个累加和(注意不能越界),如果可以,说明dp[i][j] = true

    所以,普遍位置的求法如下

            for (int i = 1; i < dp.length; i++) {
                for (int j = 1; j < max + 1; j++) {
                    dp[i][j] = dp[i - 1][j] || (j - arr[i] >= 0 && dp[i - 1][j - arr[i]]);
                }
            }
    

    经过上述处理,dp已全部填好,接下来就是判断dp中第一个为false的位置,即为答案

            for (int i = min; i <= max; i++) {
                if (!dp[arr.length - 1][i]) {
                    return i;
                }
            }
    

    如果上述过程没有找到,则返回max+1,完整代码如下

        public static int getFirstUnFormedNum(int[] arr) {
            int min = arr[0];
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                max += arr[i];
                min = Math.min(min, arr[i]);
            }
            // 可以到的范围是[min,max]
            // dp[i][j] 0....i能否组成j
            boolean[][] dp = new boolean[arr.length][max + 1];
            // 第0行 除了下述位置,其他位置都是false
            dp[0][arr[0]] = true;
            for (int i = 0; i < dp.length; i++) {
                // 0..i上一个元素都不用,可以组成0这个累加和
                dp[i][0] = true;
            }
            for (int i = 1; i < dp.length; i++) {
                for (int j = 1; j < max + 1; j++) {
                    dp[i][j] = dp[i - 1][j] || (j - arr[i] >= 0 && dp[i - 1][j - arr[i]]);
                }
            }
            for (int i = min; i <= max; i++) {
                if (!dp[arr.length - 1][i]) {
                    return i;
                }
            }
            return max + 1;
        }
    

    更多#

    算法和数据结构笔记

  • 相关阅读:
    Mybatis与Spring的集成
    高防IP:构建网络安全的重要防线
    Flask数据库_query函数的使用
    周末抽了一天时间开发了一款Storage加强插件
    Python多线程方案
    java毕业设计体育论坛网站源码+lw文档+mybatis+系统+mysql数据库+调试
    volvo s60 T4 2021款 使用手册
    JavaWeb中篇Servlet-IOC笔记
    U++ 创建UI
    字符串排序程序
  • 原文地址:https://www.cnblogs.com/greyzeng/p/16389645.html