动态规划:01背包问题(无物品价值),思想相同,题目最终要求有些变化
min为最轻物品的重量,sum为所有物品总重量
假设有一个能装入容量为C(C在[min,sum]间取值)的背包和n件重量分别为w1,w2,…,wn的物品,能否从n件物品中挑选若干件恰好装满背包,要求输出不满足条件的最小容量。
以数组{3,2,5}为例,dp初始化全为0
接下来进行逆序分析:
(逆序是因为同一件物品只能放一次)
(因为分情况时容量要减去当前物品重量,所以高容量的值要用低容量来更新,可能造成重复放入)
(举个例子,判断3是否放入时,如果是顺序的话dp[3]=dp[3]+3放了一次3,之后dp[6]=dp[3]+3又放了一次,就重复了)
判断某一物品能否放入时直接忽略容量小于该物品质量的情况
先判断3是否放入
对于容量为3及以上的都选择放入,对应dp值更新为3
再判断2是否放入
对于容量为5及以上的都选择放入,加上3,对应dp值更新为5
对于容量为3、4的如果放入后剩余容量不够放其他物品,比较3后选择较大值,对应dp值仍为3
容量为2的选择放入,对应dp值更新为2
最后判断5是否放入
对于容量为10的选择放入,加上5,dp值更新为3
对于容量为9的选择放入,加上3, dp值更新为8
对于容量为8的选择放入,加上3, dp值更新为8
对于容量为7的选择放入,加上2, dp值更新为7
对于容量为6的选择放入,dp值更新为5
对于容量为5的选择放入,dp值更新为5
在前i个状态下的最值的前提下,考虑第i个值是否使用,从而把前i个状态的最值求出来,这就是动态规划的思想
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
    int getFirstUnFormedNum(vector<int> arr, int len) {
        int sum = 0, min = arr[0];
        for (int i = 0; i < len; ++i)
        {
            sum += arr[i];
            if (arr[i] < min)
                min = arr[i];
        }
        vector<int> dp(sum + 1, 0);
        for (int i = 0; i < len; ++i)
        {
            for (int j = sum; j >= arr[i]; --j)
            {
                if (dp[j - arr[i]] + arr[i] > dp[j])
                {
                    dp[j] = dp[j - arr[i]] + arr[i];
                }
            }
        }
        for (int i = min; i <= sum; ++i)
        {
            if (i != dp[i])
                return i;
        }
        return sum + 1;
    }
};
int main()
{
    int n;
    while (cin >> n)
    {
        vector<int> a(n);
        for (int i = 0; i < n; ++i)
            cin >> a[i];
        Solution s;
        cout << s.getFirstUnFormedNum(a, n) << endl;
    }
    return 0;
}
 原文:https://blog.csdn.net/kevin980123/article/details/95651534
#include <set>
#include <vector>
using namespace std;
class Solution {
public:
 /**
 *	正数数组中的最小不可组成和
 *	输入:正数数组arr
 *	返回:正数数组中的最小不可组成和
 */
 int getFirstUnFormedNum(vector<int> arr, int len) {
 set<int> S;
        vector<int> tmp;
        int mi = 0x7fffffff;
        for(int i = 0; i < len; ++i) {
            if(arr[i] <  mi) mi = arr[i];
            for(set<int>::iterator it = S.begin(); it != S.end(); ++it) tmp.push_back(*it + arr[i]);
            for(unsigned int i = 0; i < tmp.size(); ++i)S.insert(tmp[i]);
            S.insert(arr[i]);
            tmp.clear();
        }
        for(int i = mi; ; ++i) if(S.find(i) == S.end()) return i;
        return -1;
    }
};
  class Solution {
public:
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	int getFirstUnFormedNum(vector<int> arr, int len) {
        int sum = 0, min = arr[0];
        for(int i = 0; i < len; ++i){
            sum += arr[i];
            min = min < arr[i] ? min : arr[i];
        }
        vector<int> v(sum + 1, 0);
        for(int i = 0; i < len; ++i){
            //{2, 3, 5}
            //i=0--d[10]=2 d[9]=2 d[8]=2 d[7]=2...d[2]=2
            //i=1--d[10]=5 d[9]=5...d[5]=5 d[4]=3 d[3]=3
            //i=2--d[10]=10 d[9]=8 d[8]=8 d[7]=7 d[6]=5 d[5]=5
            for(int j = sum; j >= arr[i]; --j){
                //逆序判断背包承重中能够放入的数据
                //当数组中只有2的时候,背包承重从2-10都可以放入2的数值
                //当数组中放入2和3的时候,背包承重从5-10可以放入5,3-4放入3,2只能放入2
                //当数组中放入2,3,5时,背包承重10放入10,8-9放入8,7放入7,5-6放入5...
                //dp[j-arr[i]]意思是背包承重为j时,如果已经放置了arr[i]的重量后还能放置的最大重量
                if(v[j] < v[j - arr[i]] + arr[i]){
                    v[j] = v[j - arr[i]] + arr[i];
                }
            }
        }
        //最后当承重为n时,放入的重量不为n则认为是最大不可求和
        for(int i = min; i <= sum; ++i){
            if (i != v[i]){
                return i;
            }
        }
        return sum + 1;
    }
}; #include <unordered_map>
#include <vector>
using namespace std;
class Solution
{
    public:
    int sum=0;
    void dfs(vector<int>& arr,int begin,int end,unordered_map<int,int>& hash)
    {
        if(begin>=end)return ;
        for(int i=begin;i<end;i++)
        {
            sum+=arr[i];
            hash[sum]++;
            dfs(arr,i+1,end,hash);
            sum-=arr[i];
        }
    }
    int getFirstUnFormedNum(vector<int> arr, int len) {  
        unordered_map<int,int> hash;
        dfs(arr,0,len,hash);
        int minum=0x3f3f3f3f;
        int sum=0;
        for(auto e:arr)
        {
            minum=min(minum,e);
            sum+=e;
        }
        int ret=-1;
        for(int i=minum;i<=sum;i++)
        {
            if(hash.find(i)==hash.end())
            {
                ret=i;
                break;
            }
        }
        if(ret==-1)ret=sum+1;
        return ret;
    }
};
 dp不好想,首先想到就是dfs,dfs暴力枚举,将枚举出来的子集和hash表存储,从min到sum去查询hash表,第一个未找到的就是符合题意的class Solution {
public:     /**      *    正数数组中的最小不可组成和      *    输入:正数数组arr      *    返回:正数数组中的最小不可组成和      */     int getFirstUnFormedNum(vector<int> arr, int len) {
        int min = arr[0];
        int max = 0;
        for(int i = 0; i < len; i++)
        {
            if(arr[i] < min){
                min = arr[i];
            }
            max += arr[i];
        }
        
        vector<int> dp(max+1,0); //背包的大小
        //dp[j]表示背包最大承重(与物品的质量有关),j表示背包的大小
        for(int i = 0; i < len; i++)
        {
            for(int j = max; j >= arr[i]; j--)
            {
                if(dp[j] < dp[j-arr[i]]+arr[i]){
                    dp[j] = dp[j-arr[i]]+arr[i];
                }
            }
        }
        
        for(int i = min; i <= max; i++)
        {
            if(dp[i] != i){
                return i;
            }
        }
        return max+1;
    }
};
     public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();//输入商品个数
        int Max_room = scanner.nextInt();//输入袋子容量
        int[] weight = new int[n];
        int[] price = new int[n];
        int i = 0;
        while (i < n)
            weight[i++] = scanner.nextInt();
        i = 0;
        while (i < n)
            price[i++] = scanner.nextInt();
        int[][] dp = new int[n + 1][Max_room + 1];
        for (i = 1; i <= n; i++) {
            for (int j = 1; j <= Max_room; j++) {
                if (weight[i - 1] > j) {//袋子的容量不够
                    dp[i][j] = dp[i - 1][j];
                } else {//袋子容量足够
                    dp[i][j] = Math.max(dp[i - 1][j], price[i - 1] + dp[i - 1][j - weight[i - 1]]);
                }
            }
        }
        System.out.println(dp[n][Max_room]);
    }
     public static int getFirstUnFormedNum(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        if (arr.length == 1) {//只有一个元素 ,在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和;
            return arr[0] + 1;
        }
        int min = arr[0];
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            max += arr[i];
            min = Math.min(arr[i], min);
        }
        int[] dp = new int[max + 1];
        for (int i = 0; i <arr.length ; i++) {
            for (int j = max; j >= arr[i]; j--) {
                //dp数组从后向前赋值
                dp[j] = Math.max(dp[j], dp[j - arr[i]] + arr[i]);
            }
        }
        for (int i = min; i < max; i++) {
            if (dp[i] != i)
                return i;
        }
        return max + 1;
    }
 import java.util.*;
public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
    public void getNumber(int[] arr,ArrayList<Integer> result,int pos,int sum){
        if(pos==arr.length){
            return;
        }
        for(int i = pos;i<arr.length;i++){
            sum += arr[i];
            result.add(sum);
            getNumber(arr,result,i+1,sum);
            sum -= arr[i];
        }
    }
	public int getFirstUnFormedNum(int[] arr) {
         //2种情况: 1.[min,max] 有正数不能被子集相加得到! 返回该 数
         //        2.[min,max] 所以正数都能被子集相加得到 返回 max+1
        Arrays.sort(arr);
        int min = arr[0];
        int max = arr[0];
        ArrayList<Integer> result = new ArrayList<>();
        getNumber(arr,result,0,0);
        for(int i = 1;i<arr.length;i++){
            max += arr[i];
        }
        for(int i = min+1;i<max;i++){
            if(!result.contains(i)){
                return i;
            }
        }
        return max+1;
	}
} public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	public int getFirstUnFormedNum(int[] arr) {
        int min = Integer.MAX_VALUE;
        int max = 0;
        for(int i = 0;i < arr.length;i++){
            if(arr[i] < min){
                min = arr[i];
            }
            max += arr[i];
        }
        int[] dp = new int[max + 1];
       for(int i = 0;i < arr.length;i++){
            for(int j = max;j >= min;j--){
                if(j >= arr[i]){
                    dp[j] = Math.max(dp[j],dp[j - arr[i]] + arr[i]);
                }
            }
        }
        for(int i = min;i <= max;i++){
            if(dp[i] != i){
                return i;
            }
        }
        return max + 1;
	}
} public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	public int getFirstUnFormedNum(int[] arr) {
        int min = Integer.MAX_VALUE;
        int max = 0;
        for(int i = 0;i < arr.length;i++){
            if(arr[i] < min){
                min = arr[i];
            }
            max += arr[i];
        }
        boolean[] res = new boolean[max + 1];
        res[0] = true;
        for(int t : arr){
            for(int i = max;i >= t;i--){
                res[i] = res[i] || res[i - t];
            }
        }
        for(int j = min;j < res.length;j++){
            if(!res[j]){
                return j;
            }
        }
        return max + 1;
	}
} class Solution {
     vector<vector<int>> result;// 存放所有子集序列
     vector<int>path;// 存放从跟到每个节点
void backtracing(vector<int> v,int startIndex)
    {
        if(!path.empty())
        {
            result.push_back(path);
        }
        if(startIndex>=v.size())
            return;
        for(int i=startIndex;i<v.size();i++)
        {
            path.push_back(v[i]);
            backtracing(v, i+1);
            path.pop_back();
        }
    }
    bool find(int num)
    {
        for(int i=0;i<path.size();i++)
        {
            if(i==num)
                return true;
        }
        return false;
        
    }
  
    
public:
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
  
    
	int getFirstUnFormedNum(vector<int> arr, int len) 
    {
        // 求出vector所有子集
    
        backtracing(arr,0);
        // 将path用来存放所有子集的和
         path.clear();
        for(int i=0;i<result.size();i++)
        {
            int sum=0;
            for(int j=0;j<result[i].size();j++)
            {
                sum+=result[i][j];
            }
            path.push_back(sum);
        }
        //找到子集中的最大值和最小值
        int Max=path[0];
        int Min=path[0];
        for(int i=0;i<path.size()-1;i++)
        {
            Min=min(path[i],Min);
            Max=max(path[i],Max);
        }
        for(int i=Min;i<=Max;i++)
        {
            if(find(i)==0)
            {
                return i;
            }
        }
        return Max+1;
    }
}; class Solution {
public:
    //本题可以转化为 ---> 01背包问题
    //背包容量: 数据组成的范围min ~ max
    //物品:arr的元素
    //1. 确定dp数组
    //dp[j]: 背包容量为j的背包, 背包内所放的物品的总质量是dp[j]
    //2. 状态转移方程:
    //当物品arr[i]能够放入大小为j的背包时 if(arr[i] <= j)
    //dp[j] = max(dp[j], dp[j - arr[i]] + arr[i]);
    //3. 初始化dp数组
    //dp数组开的空间大小为max + 1大小,里面的元素都初始化为0就行
    //4. 确定遍历顺序
    //使用一维滚动数组的话,需要逆序遍历背包,防止元素重复计算
    //使用二维dp的话,先遍历哪个都可以
	int getFirstUnFormedNum(vector<int> arr, int len) 
    {
        //先遍历一遍arr数组求出背包容量的范围[min, max]
        int minSize = INT_MAX, maxSize = 0;
        for(auto e : arr)
        {
            maxSize += e;
            if(minSize > e)
                minSize = e;
        }
        //这里选用了一维dp
        vector<int> dp(maxSize + 1, 0);
        for(int i = 0; i < arr.size(); ++i)
        {
            for(int j = maxSize; j >= minSize; --j)
            {
                if(j >= arr[i])
                    dp[j] = max(dp[j], dp[j - arr[i]] + arr[i]);
            }
        }
        for(int i = minSize; i <= maxSize; ++i)
        {
            if(dp[i] != i)
                return i;
        }
        return maxSize + 1;
    }
}; public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
    public int getFirstUnFormedNum(int[] arr) {
        // 全是正数的 arr
        // i 代表物品
        // j 代表背包容量
        // 目地: 找出数组中最小不可能组成和
        //       ==> 找出最小不能被填满的背包
        // 1. 得到最大最小值
        int max = 0;
        int min = Integer.MAX_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max += arr[i];
            if (min > arr[i]) {
                min = arr[i];
            }
        }
        // 2. 定义 dp 数组
        boolean[] dp = new boolean[max+1];
        // 初始化
        dp[0] = true;
        // 3. 递推过程
        for (int i = 0; i < arr.length; i++) {
            for (int j = max; j >= arr[i]; j--) {
                // 当 j == arr[i] 的时候, j - arr[i] 代表自己能够到达的下标 2-2 = 0
                // 第一个为 T 的是 3, 当 j == 3 的时候, j - arr[i] = 0;
                // 第二个位 T 的是 5, 当 j == 5 的时候, j - arr[i] = 3;
                // 第三个为 T 的是 2, 当 j == 2 的时候, j - arr[i] = 0;
                // 第四个为 T 的是 9, 当 j == 9 的时候, j - arr[i] = 5;
                // 第五个为 T 的是 7, 但 j == 7 的时候, j - arr[i] = 3;
                // 第六个为 T 的是 6, 当 j == 6 的时候, j - arr[i] = 2;
                // 第七个为 T 的是 4, 当 j == 4 的时候, j - arr[i] = 0;
                dp[j] = dp[j-arr[i]] || dp[j];
                // dp[j-arr[i]] 代表 "拿" 当前数字
                // dp[j] 代表不拿当前数字 比如 5 的时候, dp[5] || dp[5-1] 原来 dp[5] 是 T dp[1] 是 F, 所以这个时候 "不拿"
            }
        }
        // 打印数组
        // System.out.println(Arrays.toString(dp));
        // 4. 查询结果
        for (int i = min; i < dp.length; i++) {
            if (!dp[i]) {
                return i;
            }
        }
        return max + 1;
    }
} class Solution {
public:
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
	int getFirstUnFormedNum(vector<int> arr, int len) {
        set<int> s;
		s.insert(arr[0]);
		for (int i = 1; i < len; i++)
		{
			auto it = s.begin();
			set<int> t(s);
			while(it!=s.end())		
				t.insert(*(it++) + arr[i]);
			t.insert(arr[i]);
			s = t;
		}
		auto it = s.begin();
		while (it != --s.end()) {
			int t = *(it++);
			if (*it != t + 1)
				return t + 1;
		}
		return *it + 1;
    }
}; int getFirstUnFormedNum(vector<int> arr, int len) {
        if (len == 0 || arr.size() == 0)             return 1;         set<int> nums;         int sum = 0;         for (int i = 0; i < len; i++)         {             nums.insert(arr[i]);             sum = arr[i];             for (int j = i + 1; j < len; j++)             {                 sum += arr[j];                 nums.insert(sum);
                nums.insert(arr[i] + arr[j]);             }         }         set<int>::iterator itbegin = nums.begin();
        set<int>::iterator itend = nums.end();
        int num = *(--itend);
        for(int k = *itbegin; k < num; k++)
        {
            if(!nums.count(k))
                return k;
        }         return num + 1;
    }
 大佬们看一下我这个有啥问题,思路就是把所有的非空子集的和装入set中,然后查找,case通过0
                                                                                    public class Solution {
    public int getFirstUnFormedNum(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 1;
        }
        int min = Integer.MAX_VALUE;
        int max = 0;
        for (int i = 0;i < arr.length;i++) {
            min = Math.min(min, arr[i]);
            max += arr[i];
        }
        boolean[] dp = new boolean[max + 1];
        dp[0] = true;
        dp[arr[0]] = true;
        for (int i = 1;i < arr.length; i++) {
            for (int col = dp.length - 1; col-arr[i] >= 0; col--) {
                dp[col] = dp[col - arr[i]] ? true : dp[col];
            }
        }
        for (int num = min + 1; num <= max; num++) {
            if(! dp[num]) {
                return num;
            }
        }
        return max + 1;
    }
}
                                                                                    
public class Solution {               public int getFirstUnFormedNum(int[] arr) {
        
        if(arr == null || arr.length == 0)
            return 1;
        int min = Integer.MAX_VALUE;
        int max = 0;
        for (int i = 0; i < arr.length; i++) {
            max += arr[i];
            min = Math.min(min,arr[i]);
        }
        boolean form[] = new boolean[max + 1];
        form[0] = true; // 为了使单个元素去求和时是真的  (i + 0 = i)
        for (int i = 0; i < arr.length; i++) {
            for (int j = max; j >= arr[i]; j--) {
                form[j] = form[j - arr[i]] || form[j];
            }
        }
        for (int i = min; i < form.length; i++) {
            if(!form[i])
                return i;
        }
        return max + 1;     }
}
 import java.util.*;
public class Solution {
	/**
	 *	正数数组中的最小不可组成和
	 *	输入:正数数组arr
	 *	返回:正数数组中的最小不可组成和
	 */
   public int getFirstUnFormedNum(int[] arr) {
		int min = Integer.MAX_VALUE;
        int max = 0;
        for(int value: arr){
            if (value < min) min = value;
            max += value;
        }
        Arrays.sort(arr);
        BitSet[] table = new BitSet[arr.length];
        for(int i=0;i<table.length;i++){
            table[i] = new BitSet(max-min+1);
        }
        table[0].set(arr[0]-min);
        for(int i=1;i<arr.length;i++){ //array[i]
            table[i].set(arr[i]-min); //设置当前列的值arr[i],注意偏移
            for(int j=min;j<=max;j++){
                if(table[i-1].get(j-min)){
                    table[i].set(j-min); //继承上一行的值
                    if(j+arr[i]<=max) table[i].set(j+arr[i]-min); //上一行值加上这个值产生的新值
                }
            }
        }
        for(int j=min;j<=max;j++){
            if(!table[arr.length-1].get(j-min)) return j;
        }
        return max+1;
   }
}