这道题题目的意思是,给定一个 (n, a) ,求最小的 m 使得 (m, b) 与其等价
在这里「等价」的定义为:向量 a[i] 可以表示的数,b[i] 同样可以表示;对于不能表示的数也同理
我们从样例的角度出发来理解这个过程
3, 19, 10, 6
在这里面 6 和 19 都是重复的,因为 6=3+3, 19=10+3+3+3
因此这 4 个数用 2 个就可以替代,所以 m=2
也就是说,对于给定的一组数 a[1…n] ,我们需要删去所有可以由其他数表示的数,即删去所有满足 ai=ai1+ai2+⋯+aik 的 ai
我们将 a[0…n] 从小到大排序,然后考察每个数 a[i] ,如果 a[i] 可以用 a[0…i−1] 之间的加和表示,那么我们就删去该数
问题转换后,我们每个数可以使用的此时是无限次,因此这是一个完全背包求方案数的问题,我们需要判断的是方案数是否为 0
完整代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 110, M = 25000 + 10;
bool f[M];
int q[N];
int n;
int main()
{
int k;
cin >> k;
while(k--)
{
cin >> n;
for(int i = 1; i <= n; i++)
cin >> q[i];
memset(f, false, sizeof f);
f[0] = true;
sort(q + 1, q + 1 + n);
int m = q[n];
int cnt = 0;
for(int i = 1; i <= n; i++)
{
if(!f[q[i]]) cnt++;//要先判断,之后再对数组进行赋值
for(int j = q[i]; j <= m; j++)
f[j] |= f[j - q[i]];
}
cout << cnt << endl;
}
return 0;
}
我们在判断何时让 cnt 增加的时候,需要在对 f[j] 赋值之前完成
这里我们回顾 f[i][j] 的定义:对前 i 个数考虑,加和恰好为 j 的方案数
由于我们对空间进行了压缩,因此赋值前的 f[j] 表示上一层的状态,赋值后表示当前层的状态
由于我们是看在 0 到 i−1 当中有没有数能过凑出 a[i] ,也就是我们应该去看上一层的状态,所以这里的判断不能写在循环的后面