在網友的國度中共有 n 種不一樣面額的貨幣,第 i 種貨幣的面額爲 a[i],你能夠假設每一種貨幣都有無窮多張。爲了方便,咱們把貨幣種數爲 n、面額數組爲 a[1..n] 的貨幣系統記做 (n,a)。html
在一個完善的貨幣系統中,每個非負整數的金額 x 都應該能夠被表示出,即對每個非負整數 x,都存在 n 個非負整數 t[i] 知足 a[i]×t[i] 的和爲 x。然而, 在網友的國度中,貨幣系統多是不完善的,便可能存在金額 x 不能被該貨幣系統表示出。例如在貨幣系統 n=3, a=[2,5,9] 中,金額 1,3 就沒法被表示出來。數組
兩個貨幣系統 (n,a) 和 (m,b) 是等價的,當且僅當對於任意非負整數 x,它要麼都可以被兩個貨幣系統表示出,要麼不能被其中任何一個表示出。spa
如今網友們打算簡化一下貨幣系統。他們但願找到一個貨幣系統 (m,b),知足 (m,b) 與原來的貨幣系統 (n,a)等價,且 m 儘量的小。他們但願你來協助完成這個艱鉅的任務:找到最小的 m。code
輸入文件的第一行包含一個整數 T,表示數據的組數。htm
接下來按照以下格式分別給出 T 組數據。 每組數據的第一行包含一個正整數 n。接下來一行包含 n 個由空格隔開的正整數 a[i]。blog
輸入文件的第一行包含一個整數 T,表示數據的組數。string
接下來按照以下格式分別給出 T 組數據。 每組數據的第一行包含一個正整數 n。接下來一行包含 n 個由空格隔開的正整數 a[i]。it
2
4
3 19 10 6
5
11 29 13 19 17io
2
5class
在第一組數據中,貨幣系統 (2,[3,10]) 和給出的貨幣系統 (n,a) 等價,並能夠驗證不存在 m<2 的等價的貨幣系統,所以答案爲 2。 在第二組數據中,能夠驗證不存在 m<n 的等價的貨幣系統,所以答案爲 5。
研究一下樣例,發現新貨幣系統是舊貨幣系統的子集。因而大膽猜測:新貨幣系統必定是舊貨幣系統刪掉幾種面額的貨幣,而且被刪掉的面額能夠被沒被刪掉的面額表示出來。
(遵循大膽猜測不用證實原則的大佬自動跳過這段)
證實:反證法
反正就是這樣
證實完畢
若是以上結論不成立,無非存在如下兩種狀況:
一、新貨幣系統中存在舊貨幣系統所沒有的面額,記爲a[i],a[i]不能被舊貨幣系統表示出來
二、新貨幣系統中存在舊貨幣系統所沒有的面額,記爲a[j], a[j]可以被舊貨幣系統表示出來
對於第一種狀況,顯然新貨幣系統能夠表示出舊貨幣系統所表示不出的面額,由於a[i]自己就是一種舊貨幣系統所表示不出的面額,因而這種狀況不成立。
對於第二種狀況,顯然a[j]是多餘的,由於a[j]可以被表示出來,因此須要用到a[j]時能夠用表示出a[j]的這些面額代替,因此a[j]能夠刪掉,因而這種狀況也不成立。
因此,上述結論:新貨幣系統必定是舊貨幣系統刪掉幾種面額的貨幣,而且被刪掉的面額能夠被沒被刪掉的面額表示出來成立。
那麼咱們只須要求給訂貨幣系統最多能夠刪掉多少種面額。
設f[i]表示面額爲i可否被表示出來,則
f[i]=f[i]|f[j](1<=j<k,a[k]<i)
在動規循環時順便計算f[i]==false && i∈a的個數,用總數減去f[i]==false && i∈a的個數,就是答案
時間複雜度 5*107
1 #include <algorithm> 2 #include <cstring> 3 #include <cstdio> 4 int T,n,m,a[105]; 5 bool f[25005]; 6 int main() 7 { 8 9 int i,j,k; 10 scanf("%d",&T); 11 while (T--) 12 { 13 scanf("%d",&n); 14 for (i=1;i<=n;i++) scanf("%d",&a[i]); 15 m=n; 16 std::sort(a+1,a+n+1); 17 memset(f,0,sizeof(f)); 18 f[a[1]]=1; k=1; 19 for (i=a[1]+1;i<=a[n];i++) 20 { 21 for (j=1;j<=k && !f[i];j++) 22 f[i]=f[i]|f[i-a[j]]; 23 if (i==a[k+1]) 24 { 25 k++; 26 if (f[i]) m--; 27 else f[i]=1; 28 } 29 } 30 printf("%d\n",m); 31 } 32 return 0; 33 }