题目
思路
关键找到性质, 在最优解˙中每一个 b i ∈ { a 1 , a 2 , . . . , a n } b_i \in \left \{ a_1, a_2, ..., a_n \right \} bi∈{a1,a2,...,an}每一个 b i b_i bi必然和某一个 a a a相等
为什么是成立的?
反证法, 假设存在一个
b
i
b_i
bi和每一个
a
a
a都不相等,
b
i
b_i
bi可以有最优解表示出来, 由于最优解集合与原集合等价, 因此也可以使用
a
i
a_i
ai表示出来
表示为如上形式, 因为
b
i
b_i
bi不等于
a
i
a_i
ai, 因此
t
1
+
t
2
+
.
.
.
+
t
n
≥
2
t_1 + t_2 + ... + t_n \ge 2
t1+t2+...+tn≥2, 每一个
a
a
a可以由若干个
b
b
b凑出来, 因此可以将
a
a
a转化为
b
b
b的表示形式
因此可以将 b i b_i bi表示为
b 1 r 1 + b 2 r 2 + . . . + b n r n b_1r_1 + b_2r_2 + ... + b_nr_n b1r1+b2r2+...+bnrn
也就有
b
1
r
1
+
b
2
r
2
+
.
.
.
+
b
n
r
n
≥
t
1
+
t
2
+
.
.
.
+
t
n
≥
2
b_1r_1 + b_2r_2 + ... + b_nr_n \ge t_1 + t_2 + ... + t_n \ge 2
b1r1+b2r2+...+bnrn≥t1+t2+...+tn≥2
也就是 b i b_i bi可以被 b b b集合的其他数字表示, 也就不是最优解, 矛盾, 因此最优解 b i ∈ { a 1 , a 2 , . . . , a n } b_i \in \left \{ a_1, a_2, ..., a_n \right \} bi∈{a1,a2,...,an}, 也就是每一个 b i b_i bi必然属于某一个 a i a_i ai
将原集合进行从小到大排序, 对于没个位置检查能否被前面的面额表示出来, 如果可以不选择, 否则选择, 也就是完全背包问题
*超时代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 110, M = 25010;
int n, w[N];
bool check(int val, vector<int> &w) {
bool f[M] = {0};
f[0] = true;
for (int i = 1; i < w.size(); ++i) {
for (int j = w[i]; j <= val; ++j) {
f[j] |= f[j - w[i]];
}
}
return f[val];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
sort(w + 1, w + n + 1);
vector<int> ans{0};
for (int i = 1; i <= n; ++i) {
if (check(w[i], ans)) continue;
ans.push_back(w[i]);
}
cout << ans.size() - 1 << "\n";
}
return 0;
}
A C AC AC代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 110, M = 25010;
int n, w[N];
bool f[M];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
sort(w + 1, w + n + 1);
memset(f, 0, sizeof f);
f[0] = true;
int res = 0;
for (int i = 1; i <= n; ++i) {
if (f[w[i]]) continue;
res++;
for (int j = w[i]; j <= w[n]; ++j) f[j] |= f[j - w[i]];
}
cout << res << "\n";
}
return 0;
}