洛谷P2001【硬币的面值】

其实这道题就按照题面意思直接模拟一下就好喇!

很显然如果给定硬币的最小值大于1则输出"No answer!!!"因为这样子就无法取到1这个面值了。

先证明一下这一点:如果目前状态可取前 x x x 价格,且当前取了一个面额为 a a a 的硬币,要想构成前 x + a x + a x+a 的所有价格,必须仅当 x ≥ a − 1 x \ge a - 1 xa1 时成立—>

证:若 x < a − 1 x < a - 1 x<a1, 则我们取最小的 x + 1 x + 1 x+1,它必须由 x + 1 − a x + 1 - a x+1a 得到,而 x < a − 1 x < a - 1 x<a1,故 x + 1 − a < 0 x + 1 - a < 0 x+1a<0,由于不存在小于0的状态,故原命题成立。

假设当前状态,前 i i i 种硬币已经用最小的硬币数 a n s ans ans 构成最大的可行价格 t o t tot tot ,那么如果当前 t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 totai+11,那么就可以直接 i + + i + + i++;否则累加,使 t o t = t o t + a i tot = tot + a_{i} tot=tot+ai t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 totai+11 , 同时每次 a n s ans ans + + + + + + 。因为我们在每一步都满足 t o t ≥ a i + 1 − 1 tot \ge a_{i + 1} - 1 totai+11 , 所以每次 i + + i++ i++ 时,我们都可以放心操作当前的 t o t + a i tot + a_{i} tot+ai 操作。特别的,当 i i i 等于1时,如果 t o t ( = 0 ) < a 1 − 1 tot(=0) < a_{1} - 1 tot(=0)<a11 , 即 a 1 > 1 a_{1} > 1 a1>1时,不能满足条件,因而无答案,这里解释了第二段的显然结论。

关于这个累加,由于数据范围很大,我们很容易被卡(如都是1),所以我们先算出 t o t tot tot 加上多少个 a i a_{i} ai 才能大于等于 a i + 1 a_{i + 1} ai+1,即个数 k = ( a i + 1 − 2 − t o t ) ÷ a i + 1 k = (a_{i + 1} - 2 - tot) \div a{i} + 1 k=(ai+12tot)÷ai+1 ,再 t o t = t o t + k ∗ a i tot = tot + k * a_{i} tot=tot+kai a n s = a n s + k ans = ans + k ans=ans+k, 用 O(1)的时间解决了问题。同时我们把 a n + 1 a_{n + 1} an+1 赋值为 m m m , 助于判停。

最后注意一点:如果 t o t tot tot 刚好等于 m − 1 m - 1 m1 ,并结束了循环,此时我们要在循环外输出 a n s + 1 ans + 1 ans+1 , 以应对此特殊情况。

下面我来贴一下代码—>

#include <cstdio>
#include <algorithm>
#define ll long long
#define inf 1023456789

using namespace std;

ll n, m, a[2000005], ans, tot;

int main(){
	scanf("%lld%lld",&n,&m);
	for(int i = 1; i <= n; i++)
		scanf("%d",&a[i]);
	sort(a + 1, a + n + 1);
	if(a[1] != 1) {
		printf("No answer!!!\n");
		return 0;
	}
	a[n + 1] = m;
	for(int i = 1; i <= n; i++){
		if(tot < a[i + 1] - 1){
			ll k = (a[i + 1] - 2 - tot) / a[i] + 1;
			tot += a[i] * k;
			ans += k;
			if(tot >= m){
				printf("%lld\n",ans);
				return 0;
			}
		}
	}
	printf("%lld\n",ans + 1);
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值