正数数组的最小不可组成和

链接

给定一个正数数组arr,其中所有的值都为整数,以下是最小不可组成和的概念
把arr每个子集内的所有元素加起来会出现很多值,其中最小的记为min,最大的记为max
在区间[min, max]上,如果有数不可以被arr某一个子集相加得到,那么其中最小的那个数是arr的最小不可组成和
在区间[min, max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和
请写函数返回正数数组arr的最小不可组成和

import java.util.Scanner;

public class Main {

    /**
     * 时间复杂度O(n * sum)
     * 空间复杂度O(n * sum)
     *
     * @param arr
     * @return
     */
    private static int solve1(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 1;
        }

        int n = arr.length;

        int min = arr[0];
        int sum = 0;

        for (int i = 0; i < n; ++i) {
            min = Math.min(min, arr[i]);
            sum += arr[i];
        }

        /**
         * 多样本对应模型
         */
        boolean[][] dp = new boolean[n][sum + 1];

        /**
         * base case
         */
        for (int i = 0; i < n; ++i) {
            dp[i][0] = true;
        }

        dp[0][arr[0]] = true;

        /**
         * dp[i][j] 前i个元素是否能组成j
         * 1. 取第i个元素,dp[i - 1][j - arr[i]]
         * 2. 不取第i个元素,dp[i - 1][j]
         */

        for (int i = 1; i < n; ++i) {
            for (int j = min; j <= sum; ++j) {
                dp[i][j] = dp[i - 1][j];
                if (j - arr[i] >= 0) {
                    dp[i][j] |= dp[i - 1][j - arr[i]];
                }
            }
        }

        int ans = sum + 1;

        for (int i = min; i <= sum; ++i) {
            if (!dp[n - 1][i]) {
                ans = i;
                break;
            }
        }

        return ans;
    }

    /**
     * 时间复杂度O(n * sum)
     * 空间复杂度O(sum)空间压缩
     *
     * @param arr
     * @return
     */
    private static int solve2(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 1;
        }

        int n = arr.length;

        int min = arr[0];
        int sum = 0;

        for (int i = 0; i < n; ++i) {
            min = Math.min(min, arr[i]);
            sum += arr[i];
        }

        /**
         * 多样本对应模型
         */
        boolean[] dp = new boolean[sum + 1];

        /**
         * base case
         */
        dp[0] = true;
        dp[arr[0]] = true;

        /**
         * dp[i][j] 前i个元素是否能组成j
         * 1. 取第i个元素,dp[i - 1][j - arr[i]]
         * 2. 不取第i个元素,dp[i - 1][j]
         */
        for (int i = 1; i < n; ++i) {
            /**
             * 反向遍历,防止覆盖
             */
            for (int j = sum; j >= min; --j) {
                if (j - arr[i] >= 0) {
                    dp[j] |= dp[j - arr[i]];
                }
            }
        }

        int ans = sum + 1;

        for (int i = min; i <= sum; ++i) {
            if (!dp[i]) {
                ans = i;
                break;
            }
        }

        return ans;
    }


    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            int n = in.nextInt();
            int[] arr = new int[n];
            for (int i = 0; i < n; ++i) {
                arr[i] = in.nextInt();
            }
            System.out.println(solve1(arr));
        }
    }
}
posted @ 2021-10-22 17:01  Tianyiya  阅读(81)  评论(0)    收藏  举报