1. 程式人生 > >JZOJ-senior-5962. 【NOIP2018提高組D1T2】貨幣系統

JZOJ-senior-5962. 【NOIP2018提高組D1T2】貨幣系統

Time Limits: 1000 ms Memory Limits: 524288 KB

Description

在網友的國度中共有 n 種不同面額的貨幣,第 i 種貨幣的面額為 a[i],你可以假設每一種貨幣都有無窮多張。為了方便,我們把貨幣種數為 n、面額陣列為 a[1…n] 的貨幣系統記作 (n,a)。

在一個完善的貨幣系統中,每一個非負整數的金額 x 都應該可以被表示出,即對每一個非負整數 x,都存在 n 個非負整數 t[i] 滿足 a[i]×t[i] 的和為 x。然而, 在網友的國度中,貨幣系統可能是不完善的,即可能存在金額 x 不能被該貨幣系統表示出。例如在貨幣系統 n=3, a=[2,5,9] 中,金額 1,3 就無法被表示出來。

兩個貨幣系統 (n,a)和 (m,b) 是等價的,當且僅當對於任意非負整數 x,它要麼均可以被兩個貨幣系統表出,要麼不能被其中任何一個表出。

現在網友們打算簡化一下貨幣系統。他們希望找到一個貨幣系統 (m,b),滿足 (m,b) 與原來的貨幣系統 (n,a) 等價,且 m 儘可能的小。他們希望你來協助完成這個艱鉅的任務:找到最小的 m。

Input

輸入檔案的第一行包含一個整數 T,表示資料的組數。

接下來按照如下格式分別給出 T 組資料。 每組資料的第一行包含一個正整數 n。接下來一行包含 n 個由空格隔開的正整數 a[i]。

Output

輸出檔案共有 T 行,對於每組資料,輸出一行一個正整數,表示所有與 (n,a) 等價的貨幣系統 (m,b) 中,最小的 m。

Sample Input

2
4
3 19 10 6
5
11 29 13 19 17

Sample Output

2
5

Data Constraint

在這裡插入圖片描述

Hint

在第一組資料中,貨幣系統 (2,[3,10])和給出的貨幣系統 (n,a) 等價,並可以驗證不存在 m<2的等價的貨幣系統,因此答案為 2。
在第二組資料中,可以驗證不存在 m<n的等價的貨幣系統,因此答案為 5。

Solution

不難發現,留下的數的集合一定是原數的子集
揹包一下,維護每個數最多能由多少個比它小的原有的數組合成
組成個數=1的總數就是答案

Code

#include
<algorithm>
#include<cstring> #include<cstdio> #include<cctype> #define fo(i,a,b) for(int i=a;i<=b;++i) #define fd(i,a,b) for(int i=a;i>=b;--i) using namespace std; const int N=105,M=5e4+5; int T,n,mx; int a[N],bz[M],f[M],g[M]; inline void read(int &n) { int x=0,w=0; char ch=0; while(!isdigit(ch)) w|=ch=='-',ch=getchar(); while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); n=w?-x:x; } int solve() { memset(bz,0,sizeof(0)); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); fo(i,1,n) f[a[i]]=1,g[a[i]]=1,bz[a[i]]=1; fo(j,0,mx) if(f[j]) { fo(i,1,n) { if(j+a[i]>mx) break; f[j+a[i]]=1,g[j+a[i]]=max(g[j+a[i]],g[j]+1); if(g[j+a[i]]>1) bz[j+a[i]]=0; } } int ans=0; fo(i,1,n) if(a[i]!=a[i-1]&&bz[a[i]]) ++ans; return ans; } int main() { freopen("money.in","r",stdin); freopen("money.out","w",stdout); read(T); while(T--) { read(n),mx=0; fo(i,1,n) read(a[i]),mx=max(mx,a[i]); sort(a+1,a+1+n); printf("%d\n",solve()); } }