这是一个集合问题?
结论只有一点:任何一张可以被另外的纸币表示出来的纸币都可以被删去
比如:货币系统
(
3
,
[
2
,
3
,
5
]
)
(3,[2,3,5])
(3,[2,3,5])与货币系统
(
2
,
[
2
,
3
]
)
(2,[2,3])
(2,[2,3])等价
感性的证明比较好想:
首先,一张纸币
x
x
x能被删去当且仅当它的所有倍数都可以被其它的纸币表示
然而,如果我们拼凑出了
k
x
kx
kx,
(
k
+
1
)
x
(k+1)x
(k+1)x就一定要通过
k
x
+
x
kx+x
kx+x的形式得到,否则我们需要无限多的钱币来凑出无限多中形如
k
x
kx
kx的数值
所以你看见加号后面那个
x
x
x了吗?
证明完之后,我们就可以用完全背包解决了。
你看两万五的数据范围一看就是背包什么的嘛
还有一个不知道性能如何的优化:
给这些数排个序,然后在枚举每一个数之前先检查它是否可以被表示——可以就不用枚举它了。
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline void read(int &x)
{
x=0;register char c=getchar();
while(c<48||57<c) c=getchar();
for(;48<=c&&c<=57;c=getchar()) x=x*10+(c&15);
}
int a[110];
bool v[26000];
void Main()
{
int n;read(n);
for(int i=1;i<=n;i++)
read(a[i]);
sort(a+1,a+n+1);
memset(v+1,0,sizeof(v)-1);
int ans=n;
for(int i=1;i<=n;i++)
{
if(v[a[i]]){ans--;continue;}
for(int j=a[i];j<=a[n];j++)
v[j]|=v[j-a[i]];
}
printf("%d\n",ans);
}
int main()
{
*v=1;
int T;read(T);
while(T--)
Main();
return 0;
}