链接:正数数组的最小不可组成和__牛客网
来源:牛客网
给定一个正数数组arr,其中所有的值都为整数,以下是最小不可组成和的概念
- 把arr每个子集内的所有元素加起来会出现很多值,其中最小的记为min,最大的记为max
- 在区间[min, max]上,如果有数不可以被arr某一个子集相加得到,那么其中最小的那个数是arr的最小不可组成和
- 在区间[min, max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和
请写函数返回正数数组arr的最小不可组成和
时间复杂度为O(n×∑i=1narri)O(n \times \sum_{i=1}^n arr_i)O(n×∑i=1narri),额外空间复杂度为O(n×∑i=1narri)O(n \times \sum_{i=1}^n arr_i)O(n×∑i=1narri)
输入描述:
第一行一个整数N,表示数组长度。
接下来一行N个整数表示数组内的元素。
输出描述:
输出一个整数表示数组的最小不可组成和
示例1
输入
3
2 3 9
输出
4
示例2
输入
3
1 2 4
输出
8
说明
3 = 1 + 2
5 = 1 + 4
6 = 2 + 4
7 = 1 + 2 + 4
备注:
1 <= N <= 100
整数数组最大和不超过10000
解析:
根据题目要求的输出,很容易可以想到使用动态规划的方法来解决问题。
首先,我们要确定动态规划所用的cnt数组的长度——max + 1,通过对整数数组进行一次遍历,我们可以很容易求出该数组的子集的最大和 max 与最小和 min 。
将整数数组arr的值依次放入cnt中进行运算。
在进行运算时,我们需注意以下几点:
1.将cnt数组看做一组背包的集合,cnt中每个元素的下标即为其背包的容量。2.将整数数组arr的每个元素看做与其值对应大小的填充物。
3.尽可能大的填充cnt中的每个背包
4.整数数组arr中每个元素在每个背包最多使用一次。
我们以示例2、3、9 做出如下表格:
2 | 3 | 9 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | |
变量2 | 0 | 0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
变量3 | 0 | 0 | 2 | 3 | 3 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 | 5 |
变量9 | 0 | 0 | 2 | 3 | 3 | 5 | 5 | 5 | 5 | 9 | 9 | 11 | 12 | 12 | 14 |
其中:
2 = 2
3 = 3
5 = 2 + 3
9 = 9
11 = 2 + 9
12 = 3 + 9
14 = 2 + 3 + 9
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> arr(n);
int max = 0;
for (int& i : arr) {
cin >> i;
max += i;
}
sort(arr.begin(), arr.end());
int min = arr[0];
vector<int> cnt(max + 1, 0);
//将arr中每个元素依序计算
for (int i = 0; i < arr.size(); ++i) {
//从最大值开始,向后遍历cnt数组,知道第arr[i]个元素
//k >= arr[i] : cnt中每个元素下标即为其背包容量,
// k < arr[i]部分自然放不下大小arr[i]的元素。
//--k : 由于循环内的判断方法,从后向前遍历是为了防止元素的重复放入。
// 如,当arr[i] = 2时,如果正序遍历,在k = 2时,cnt[2] = 2。
// 但在k = 4时,cnt[2] + arr[i] 大于 cnt[4] 且小于等于 4
// 导致了2的重复使用。
for (int k = max; k >= arr[i]; --k) {
//cnt[k - arr[i]] : 从大小为k的背包中提前占用arr[i]的空间
// 对于剩下的空间,我们可以直接在cnt中查询先前对它的尽可能填充后的值。
//如果将arr[i]的物品放入背包,如果此时的值大于当前的值,则更新值。
if (arr[i] + cnt[k - arr[i]] > cnt[k]) {
cnt[k] = cnt[k - arr[i]] + arr[i];
}
}
}
for (int i = min; i < max; ++i) {
if (i != cnt[i]) {
cout << i << endl;
return 0;
}
}
cout << max + 1 << endl;
return 0;
}
代码纯享版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> arr(n);
int max = 0;
for (int& i : arr) {
cin >> i;
max += i;
}
sort(arr.begin(), arr.end());
int min = arr[0];
vector<int> cnt(max + 1, 0);
for (int i = 0; i < arr.size(); ++i) {
for (int k = arr[i]; k <= max; ++k) {
if (arr[i] + cnt[k - arr[i]] > cnt[k]) {
cnt[k] = arr[i] + cnt[k - arr[i]];
}
}
}
for (int i = min; i < max; ++i) {
if (i != cnt[i]) {
cout << i << endl;
return 0;
}
}
cout << max + 1 << endl;
return 0;
}