动态规划: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;
}
}