还在补暑假作业。
题目描述
你有一个由 N N N 种面值的货币组成的货币系统。定义两个货币系统等价,当且仅当 ∀ x ∈ N ∗ \forall x\in\N^* ∀x∈N∗ 要么同时能被两个货币系统表示,要么同时不能被表示。尝试从 N N N 种面值中删除尽量多种,使得删除后得到的新系统与原系统等价。求新系统的面值种数。
Solution
一种很显然的想法是,比如 { 2 , 3 , 5 } \{2,3,5\} {2,3,5},因为 2 + 3 = 5 2+3=5 2+3=5,所以每次我想用 5 5 5 的时候我都可以用 2 + 3 2+3 2+3 代替,所以 5 5 5 是废的。换句话说,如果一个数可以表示成其他若干个数的和,那么这个数应该被删除。
考虑用深度优先搜索实现这一步骤,可以得到 80 分。时间复杂度 O ( T n n ) O(Tn^n) O(Tnn)。
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int MAXN=110;
const int MAxm=25010;
int T,n;
int a[MAXN+10];
int dfs(int x,int y){
if(x<0) return 0;
if(!x) return 1;
int c=0;
for(int i=1;i<=n;++i){
if(!c&&y!=i) c=dfs(x-a[i],y);
if(c) break;
}
return c;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
int ans=n;
for(int i=1;i<=n;++i)
ans-=dfs(a[i],i);
printf("%d\n",ans);
}
}
设 f [ i ] f[i] f[i] 表示 i i i 这个面值是否可以被表示出来。显然,瓶颈就是求解 f f f 数组。
考虑使用动态规划优化此过程。先将
a
a
a 数组排序,然后
∀
a
[
i
]
\forall a[i]
∀a[i] 有
f
[
j
]
=
f
[
j
]
or
f
[
i
−
a
[
j
]
]
f[j]=f[j]\text{ or }f[i-a[j]]
f[j]=f[j] or f[i−a[j]]
这样就可通过此题。设
max
a
=
M
=
25
000
\max a=M=25\ 000
maxa=M=25 000,则时间复杂度为
O
(
T
M
2
)
O(TM^2)
O(TM2)。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int MAXN=110;
const int MAXM=25010;
int T,n;
int a[MAXN+10];
int f[MAXM+10];
int dfs(int x,int y){
if(x<0) return 0;
if(!x) return 1;
int c=0;
for(int i=1;i<=n;++i){
if(!c&&y!=i) c=dfs(x-a[i],y);
if(c) break;
}
return c;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
std::sort(a+1,a+n+1);
memset(f,0,sizeof(f));f[0]=1;
int ans=n;
for(int i=1;i<=n;++i){
if(f[a[i]]){
--ans;
continue;
}
for(int j=a[i];j<=a[n];++j)
f[j]=f[j]||f[j-a[i]];
}
printf("%d\n",ans);
}
}