[NOIp2018] luogu P5020 货币系统

还在补暑假作业。

题目描述

你有一个由 NN 种面值的货币组成的货币系统。定义两个货币系统等价,当且仅当 xN\forall x\in\N^* 要么同时能被两个货币系统表示,要么同时不能被表示。尝试从 NN 种面值中删除尽量多种,使得删除后得到的新系统与原系统等价。求新系统的面值种数。

Solution

一种很显然的想法是,比如 {2,3,5}\{2,3,5\},因为 2+3=52+3=5,所以每次我想用 55 的时候我都可以用 2+32+3 代替,所以 55 是废的。换句话说,如果一个数可以表示成其他若干个数的和,那么这个数应该被删除。

考虑用深度优先搜索实现这一步骤,可以得到 80 分。时间复杂度 O(Tnn)O(Tn^n)

#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] 表示 ii 这个面值是否可以被表示出来。显然,瓶颈就是求解 ff 数组。

考虑使用动态规划优化此过程。先将 aa 数组排序,然后 a[i]\forall a[i]f[j]=f[j] or f[ia[j]]f[j]=f[j]\text{ or }f[i-a[j]]
这样就可通过此题。设 maxa=M=25 000\max a=M=25\ 000,则时间复杂度为 O(TM2)O(TM^2)

#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);
	}
}
posted @ 2019-08-27 09:30  TeacherDai  阅读(119)  评论(0)    收藏  举报