嘘~ 正在从服务器偷取页面 . . .

洛谷P2001 硬币的面值 题解


洛谷P2001 硬币的面值 题解

题目链接:P2001 硬币的面值

题意

小 A 有 \(n\) 种硬币,现在要买一样不超过 \(m\) 元的商品,他不想得到找钱(多脏啊),同时又不想带太多的硬币,且硬币可以重复,现在已知这 \(n\) 种硬币的价值,请问最少需要多少硬币就能组合成所有可能的价格

输入格式

第一行两个数:\(n, m\)

下一行,共 \(n\) 个数字,表示硬币的面值。

输出格式

一行一个数,表示最少需要多少硬币。如果无解请输出 No answer!!!

数据范围

\(1 \le n \le 2 \times {10}^5,~1 \le m \le 2^{63}\)

首先如果面额里没有 \(1\) ,那一定无解,否则一定有解。

考虑将所有面额(包括 \(m\))从小到大排序,依次每个 \(a_i\)\(a_{i+1}\) 的贡献

容易发现,如果我们当前可以覆盖区间 \([1,x]\)\(a_i \le x+1\) ,那我们也能覆盖 \([1,x+ka_i]\)

而当 \(x + ka_i + 1\ge a_{i+1}\) 时,我们已经没有必要再使用 \(a_i\) 了,因为此时 \(a_{i+1}\) 能覆盖的更远

我们只需要维护 \(x\) 和硬币总数,直到超过 \(m\) 即可。

时间复杂度 \(\mathcal{O}(n \log n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(2e6 + 15))

int a[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int n,m; cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    a[n + 1] = m; sort(a + 1, a + 1 + n + 1);
    if(a[1] != 1) return cout << "No answer!!!\n", 0;
    int mx = 0, res = 0;
    for(int i = 1; i <= n; i++)
        if(mx < a[i + 1] - 1)
        {
            int x = (a[i + 1] - 1 - mx - 1) / a[i] + 1;
            mx += a[i] * x; res += x;
            if(mx >= m) return cout << res << '\n', 0;
        }
    cout << res + 1 << '\n';
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/kkksc03/solution-p2001


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录