牛客——货币系统(思维+DP)

题意:

求一个集合b,使得该集合能够表示出的数与给出的集合a能够表示出的数相同,输出该集合最少有多少个元素。

思路:

首先我们可以知道,如果集合里的一个数可以由集合里的其他数表示出来,那么前者就没有存在的必要了。

所以,在最优解里,集合b里面的元素一定是在集合a里的,因为只有这样,才能最小化集合b的元素个数。可以这样想,如果集合b里面的某个元素b[1]是由a[1] + a[2] 而来,而且a[1]、a[2]又可以由集合b的元素表示出来,那么b[1]的存在就没有意义了。

综上,可以从小到大排序后,判断a[i]能否被前面的数表示出来,如果可以的话,一定不选这个数;否则,一定要选这个数。之后借鉴完全背包的思想,进行状态转移,统计个数即可。

代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
typedef pair<int,int>PII;

inline ll read(){
    ll x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9'){
        x = (x<<1) + (x<<3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

const int maxn=25100;

int n,a[maxn];
bool dp[maxn];

int main(){
    int t=read();
    while(t--){
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        sort(a+1,a+1+n);
        memset(dp,0,sizeof dp);
        dp[0]=1;
        int res=0;
        for(int i=1;i<=n;i++){
            if(!dp[a[i]]) res++;
            for(int j=a[i];j<=a[n];j++)
                dp[j]|=dp[j-a[i]];
        }
        cout<<res<<endl;
    }
    return 0;
}