原题

P5020 [NOIP2018 提高组] 货币系统


思路概述

题意分析

给定包含一个整数 \(n\) 和一个正整数集合 \(a\) 的货币系统 \((n,a)\),要求将其化简,输出最简的货币系统中的面值数量。其中,化简在货币系统 \((n’,a’)\) 中,任意被原货币系统 \((n,a)\) 表示出的面值都能被表示,任意不能被原货币系统表示的面值相应地不能被表示;最简即 \(n’\) 最小。

思路简述

死去的小凯开始攻击我。

乍一看有点像小凯的疑惑这道题,但是本题的思路与其有一定差别。

由于要求使简化结果中 \(n’\) 尽量小,所以不难知道,若原整数集合中出现 \(\exists a_i∈a,a_i={k_1a_1+k_2a_2\dots k_{i-1}a_{i-1}}\) 的情况,就可以将 \(k\) 约去。

因此只需要枚举上述能被约去的面值,即可得到最简的货币系统。


算法实现

关于枚举

由于本题数据规模是 \(1≤a_i≤25000\),所以可以采用类似质数筛的线性标记法。需要注意的是,区别于质数筛,本题需要从一个已经可以凑出的面额上再累加枚举的面值,即:

\[\exists v_i=1⇒v_{i+kj}=1(k∈N,j∈a) \]

当出现当前选定的面额 \(i\) 出现 \(v_i=1\) 的情况,则说明 \(i\) 是可以被简化的面额,因此减小 \(n’\)

关于标记

特殊地,由于所有面额的货币数量均为 \(0\) 时,可以凑出面额 \(0\),因此标记数组初始化时就需要令 \(v_0=0\)


AC code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
const int maxn=1e2+10,maxsize=5e5+10;
int T,n,lim,ans;
int a[maxn];
bool v[maxsize];
int main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	for(cin >> T;T;--T)
	{
		memset(v,0,sizeof(v));
		cin >> n;ans=n;lim=0;
		for(RI i=1;i<=n;++i) 
		{
			cin >> a[i];
			lim=max(lim,a[i]);
		}
		sort(a+1,a+n+1);
		v[0]=1;
		for(RI i=1;i<=n;++i)
			if(!v[a[i]])
			{
				for(RI j=0;j<=lim;++j)
					if((!j) || (v[j]))
						for(RI k=1;j+a[i]*k<=lim && !v[j+a[i]*k];++k)
							v[j+a[i]*k]=1;
			}
			else --ans;
		printf("%d\n",ans);
	}
	return 0;
}

原文地址:http://www.cnblogs.com/frkblog/p/16817262.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性