2018D1T2 货币系统
2018D1T2 货币系统
problem
记货币种类为\(n\),面额数组为\(a[1...n]\)的货币系统为\((n,a)\)。货币系统\((n,a)\)与\((m,b)\)等价,当且仅当对于\(\forall x\in \mathbf N\),\(x\)要么均可以被两个货币系统表示,或者不能被任何一个表示。
给定一个货币系统\((n,a)\),求与其等价的货币系统\((m,b)\),并使\(m\)尽可能小。
solution
我们把货币系统\((n,a)\)看做集合\(A\),货币系统\((m,b)\)看做集合\(B\),我们猜测\(B⊆ A\),接下来进行证明。
一些奇奇怪怪的证明
证明1:\(A\)集合内不能被其他数表示的数必然存在于\(B\)集合内。
证明:我们设\(x\in A\)且\(x\)不能被\(A\)集合中除他以外的元素表示出来。(即\(\forall Y⊆A,\exists x\in A,x\notin T,x\neq \sum\limits_{a\in T} a\))
我们假设\(x\notin B\),由于两个集合等价,所以\(B\)集合一定能够表示出\(x\),换句话说\(\exists X⊆B,x=\sum\limits_{b\in X}b\)
那么\(X\)集合中的元素中至少有一个不在\(A\)内,并且不能被集合\(A\)里的元素组成(因为如果不存在的话\(A\)内的元素就可以表示\(x\)了)。这样与\(B\)集合的定义产生矛盾。这样我们可以证明。
证明2:\(B⊆A\)
证明:假设\(\exists x\in B,x\notin A\)
根据题目条件,\(x\)一定可以被\(A\)集合内的数字所组成。那么必然\(\exists U⊆A,U={a_i}\)可以用来组成\(x\)并且这些元素都不能被\(A\)集合内的元素组成(因为如果\(a_i\)能被\(A\)集合内其他数组成,那么必然可以用那些数来代替\(a_i\)),根据上一个结论我们知道\(U⊆B\).
那么也就是说\(x\)可以被\(B\)集合内的数字组成,那么凡是可以用\(x\)组成的数都可以被\(B\)集合内的其他数字组成,那么也就是说即便从集合\(B\)中去掉\(x\)元素也依旧满足条件,这就与\(B\)集合的定义矛盾。
所以\(\forall x\in B,x\in A \iff B⊆A\)
算法
最后:在\(A\)集合中,能被其他数组成的数必然不在\(B\)集合内
到这里,我们只需要计算出\(A\)集合内存在多少个能被其他数计算出来的就行了。
使用完全背包。
根据常识,我们知道\(x\)只能被他小的数字组成。
我们对数组排个序,然后对于每一个数考虑能不能被它前面的数字所组成。我们知道如果\(x\)能够被前\(i\)个数字组成,并且组成\(x\)的数中包含\(a_i\),那么\(x-a_i\)也可以用前\(i\)个数来组成。那么我们很容易想到定义\(f(x)\)表示\(x\)能否被组成,则\(f(x)=f(x)∨f(x-a_i)\)
code
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
#define MAXAI 25005
#define MAXN 105
int f[MAXAI];
int a[MAXN];
int main()
{
int i,j,n,T,ans;
scanf("%d",&T);
while(T--)
{
memset(f,0,sizeof(f));
scanf("%d",&n);ans=n;
for(i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
f[0]=1;
for(i=1;i<=n;i++)
{
if(f[a[i]])
{
ans--;
continue;
}
for(j=a[i];j<=a[n];j++)
{
f[j]=f[j]|f[j-a[i]];
}
}
printf("%d\n",ans);
}
return 0;
}