思路:这道题可以通过每一步的规划进而推出全局总共可以组成的数字的个数,即从取到零个数字为止可以组成一个数字0到取到n个数字为止可以组成m个数字,下图意为取到i个数字为止是否可以组成数字j,使用布尔型数组统计次数,1代表可以组成。当上一步可以组成的数字在下一步多取一个数时依然可以组成,并且在增加了a[i]这个数字后,第j+a[i]个也可以被组成(这时j的取值是上一步中所有为true的j范围)
但是使用二维bool型数组,空间会达到5e5的内存,空间太大,这时候将二维数组简化为一维,在原数组中进行复制和新增的操作,使用样例中三个数1 1 5进行演示
接下来做进一步简化,使用类似于数组的一个二进制结构bitset,只可以存放二进制数,可以大量节省空间
代码如下:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 5e3 + 9, M = 5e5 + 9;
int a[N];
void solve() {
int n; cin >> n;
for (int i = 1; i <=n; ++i)cin >> a[i];
bitset<M>bs;
bs[0] = 1;//最低位置为1,代表刚开始取零个数时,只可以组成0一个数的初始状态
for (int i = 1; i <=n; i++) {
bs |= (bs<< a[i]);//bs与bs-a[i]进行或操作,就可以同时进行复制与新增的操作,即把上一次的状态的复制过来,并且对在增加了a[i]后还可以组成哪些数进行标记,<<是位运算,相当于减
}
cout << bs.count() << '\n';//将数组中标记的个数输出
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int _ = 1;
while (_--)solve();
return 0;
}