找零钱
大约 3 分钟
找零钱
背景
在某天下午cbl叫我看一个题,[C. 找零问题(再次加强版)](C. 找零问题(再次加强版) - 2022小学期——Day03 - 比赛 - XJTUOJ (xjtuicpc.com)),浅浅写了一下,不出意外的WA了。随后凯爹的代码就来了,我看了一下,没怎么看懂,我又觉得我的思路没问题,于是我就开启了自己造数据,用凯爹的代码来找自己代码的问题,最后找出来了。但是经历了前一天晚上那场痛苦的cf,于是乎我暴躁起来了,看着凯爹空间复杂度,我就开启了自己出题之旅……
思路
对于类似的找钱的问题,第一眼看上去要么是贪心,要么是dp,不出意外,本题有贪心的思想。
首先,对于组成区间中所有数字,我们可以知道,于是有了不成立的条件.当时,区间中所有数字都可以被组成,只是个数问题。
对于用数组中的元素组成,且数量最少我们自然而然想到贪心,即先选数值大的钱币。所以首先的需要对数组进行排序。然后,我们假设中只有两个元素,则钱币个数:
假设恰好等于时,此时所需的纸币数量最少为张(张面值为和张面值为),此时,中的数字都可以被组成。
对于的情况,设,为手中钱币面值和,若,,我们可以得到中的数都可以被组成,此时:
为了使最大时纸币数量最少,我们尽可能选取面值最大的纸币,需要补足的钱币张数为:
再将钱币数量加上即可得到最终答案。
第一个减一为到的差值,再进行向上取整的除法
if (t<a[i]-1)
{
y = (a[i] - t - 2) / a[i - 1] + 1;
res += y;
t += y * a[i - 1];
}
if (t<a[i])
{
res++;
t += a[i];
}
直到纸币大小枚举完或者时停止,对剩下的值贪心补差值:
if (t<x)
{
y = (x - t - 1) / a[i - 1] + 1;
res += y;
}
完整代码
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define WA return 0;
using namespace std;
inline ll read() { ll x = 0, z = 1; char c = getchar(); while (!isdigit(c)) { if (c == '-')z = -1; c = getchar(); }while (isdigit(c)) { x = (x << 1) + (x << 3) + (c ^ 48); c = getchar(); }return z * x; }
inline void writ(ll x) { if (x < 0) { putchar('-'); x = (~x) + 1; }if (x > 9)writ(x / 10); putchar(x - x / 10 * 10 + 48); }
const int N=1e6+5;
ll n, x;
ll a[N] = { 0 };
ll res = 1; //res为钱币数量
void Qingtuan() {
n = read(); x = read();
for (int i = 1; i <= n; i++)
{
a[i] = read();
}
sort(a + 1, a + n + 1);
if (a[1] != 1)
{
printf("-1");
return;
}
int i; ll t = 1; ll y; //t为此时手中钱币价值和
for (i = 2; i <= n; i++)
{
if (t >= x||a[i]>x)
{
break;
}
if (a[i] <= t)
{
continue;
}
if (t<a[i]-1)
{
y = (a[i] - t - 2) / a[i - 1] + 1;
res += y;
t += y * a[i - 1];
}
if (t<a[i]) //此时t==a[i]-1
{
res++;
t += a[i];
}
}
if (t<x)
{
y = (x - t - 1) / a[i - 1] + 1;
res += y;
}
writ(res);
}
int main() {
//ios::sync_with_stdio(false);
//cin.tie(0);cout.tie(0);
//freopen("data.in","r",stdin);
//int T=read();while (T--)
Qingtuan();
//fclose(stdin);
WA
}