题目
简化题意
给你一个集合 (A),让你找到元素最少的和集合 (A) 等价的集合 (B)。
等价指的是用 (A) 中元素能表出的使用 (B) 中元素也能表出,用 (A) 中元素不能表出的使用 (B) 中元素也不能表出。
题解
dp。本题的答案就是 (A) 中不能被 (A) 中其它元素表出的元素的个数。
证明:
先证明 (A) 中不能被 (A) 中其它元素表出的元素一定在 (B) 中。
反证法。若 (x in A) 并且 (x) 不能被 (A) 中其它元素表出。设 (x otin B)。
因为 (A) 和 (B) 是等价的,所以 (B) 中一定存在 (b_1,b_2,dots b_k) 可以表出 (x)。然后 (A) 中一定存在元素可以表出 (b_1,b_2,dots, b_k),那么 (x) 可以由 >(A) 中元素表出,与事实不符。证毕。
再证明 (A) 中能被 (A) 中其它元素表出的元素一定不在 (B) 中。
反证法。若 (x in A) 并且 (x) 能被 (a_1,a_2,dots,a_k) 表出。设 (x in B)。
因为 (A) 和 (B) 是等价的,所以 (B) 中元素可以表出 (a_1,a_2,dots,a_k),也就可以表出 (x)。与 (B) 中元素尽量少的事实不符。证毕。因为以上两点,所以本题的答案就是 (A) 中不能被 (A) 中其它元素表出的元素的个数。证毕。
将 (A) 中元素从小到大排序,(f_i) 表示用一部分数能否表示出 (i)。
最小的元素一定不能被其它元素表示,所以用最小的元素去更新 (f) 数组,然后每次更新后可以判断出 (A) 中下一个元素能否被表示。不断更新即可。
Code
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 25001
int t, n, ans, a[101], f[M];
int main() {
scanf("%d", &t);
while (t--) {
memset(f, 0, sizeof f);
scanf("%d", &n), ans = n, f[0] = 1;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
std::sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) {
if (f[a[i]]) --ans;
else {
for (int j = a[i]; j <= a[n]; ++j) {
f[j] |= f[j - a[i]];
}
}
}
printf("%d
", ans);
}
return 0;
}