貨幣系統 (完全揹包)

https://ac.nowcoder.com/acm/problem/21228

首先我們要想到,最後的最小的貨幣系統應該是原貨幣系統的一個子集,原因也很簡單——如果a能夠用其他一個或若干個面值表示那麼它顯然不必要,而如果你去引入一個原來不存在的數那麼要麼是多於的(能被原來貨幣系統裏面的一個或多個數表達)要麼就會引入新的東西。
所以現在我們要做到就是在剛剛的集合裏面求所有必要的無法被替代的貨幣數量。
顯然,最小的數肯定要留下,因爲其他數字肯定表示不了它。
然後第二大的數,如果是最小的數的倍數就不需要,不是就得留下。
再考慮第三大的,假設前兩個數都留下了,顯然,如果它是前兩個數能組成的數那肯定就可以刪掉了。
依此類推,每個數如果能被小於它的數組成,就刪掉。
現在問題就變成了:給你若干個數,他們每個都可以使用無窮多次,問能組成的數有哪些——這不就是個典型的完全揹包麼?只是把最大價值變成可能性問題了而已,於是就很好解決了。
f[i]表示當前這個數x之前的數能不能組成i,如果f[x]等於1,那麼說明x可以刪了,刪掉即可;如果f[x]是0,那麼x不能刪,就按完全揹包的方式更新f數組。

#include<bits/stdc++.h>
using namespace std;
int a[100005];
bool vis[100005];
int main(){
    int t;
    cin>>t;
    while(t--){
        int ans=1;
        memset(vis,0,sizeof(vis));
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>a[i];
        sort(a+1,a+1+n);
        for(int i=1;i*a[1]<=a[n];i++)
            vis[i*a[1]]=1;
        for(int i=2;i<=n;i++){
            if(vis[a[i]])
                continue;
            ans++;
            vis[a[i]]=1;
            for(int j=a[i]+1;j<=a[n];j++){
                if(!vis[j]&&vis[j-a[i]])
                    vis[j]=1;
            }
        }
        cout<<ans<<endl;
     }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章