开篇点题:参考彩笔大佬的题解!
题目分析:
给定一个集合 (n,a) ------------- 表示给定n个a[i] 求出一个集合(m,b) -------------- 表示所求的m个b[j],使得每个a[i]都可以被b[j]表示,并且也可以表示所有通过a数组线性表示的值,同时也可由m个数表示。
通过题目所说表示数的方式,可以推断出是完全背包的做法。(即用无限个可用物品承装体积)
比较困难的是部分线性代数知识:
通过大一上学期学过的线性代数,
我们可以通过k = x1a1 + x2a2 + x3a3 + x4a4 ······ + xnan (任意x >= 0且不同时为0)来表示所能表示的整数集。如果a[i]中的某个数可以被更小的k(其他的整数表示),则该a[i]是无用的,因此我们只需求出最大无关组,使得每个a都可以被更小的m表示出来即可。
那如何求向量组 (a1,a2,⋯,an) 的 最大无关向量组?
利用数论中素数筛的思路,对于一个较大的a[i],如果出现重复,一定会被较小的a[i]删掉。
则我们首先进行排序:
从小到大,先查看当前数有没有被晒掉,
1)如果没有就把它加入到最大无关向量组中,并把他以及他和此前的硬币的线性组合都筛掉
2)如果有就不理会
3)即就是在完全背包求方案数的过程中,统计那些初始没有方案数的物品
因此就是一个筛的写法!
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define PII pair<int, int>
#define PLL pair<ll, ll>
const int N = 1e6 + 10;
const int M = 2 * N;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const double eps = 1e-8;
int a[N];
int f[N];//考虑前i个物品,当前体积为j的方案
void solve()
{
int n;
cin >> n;
for(int i = 0; i < n; i ++) cin >> a[i];
sort(a,a+n);
int m = a[n - 1];
int res = 0;
memset(f, 0, sizeof f);
f[0] = true;//一个不选,0可以被表示
for(int i = 0; i < n; i ++)
{
if(f[a[i]]) continue;
else res ++;
for(int j = a[i]; j <= m; j ++)
{
f[j] |= f[j - a[i]];
}
}
cout << res << endl;
}
int main()
{
int t;
cin >> t;
while(t --)
solve();
system("pause");
return 0;
}