1 条题解
-
0
自动搬运
来自洛谷,原作者为

qwaszx
不再为往事受困.搬运于
2025-08-24 21:27:37,当前版本为作者最后更新于2018-08-20 12:17:49,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
思路:贪心
将面值从小到大排序
考虑用前种面值可以凑出的最大价值
显然当且仅当时是无解的,因为凑不出
这样就限定了
假设已经凑出了~的面值,那么我们可以加入一张面值不超过的硬币
如果超过,那么就不可以凑出面值
设这个面值是,那么可以把延伸到
显然这个面值越大越好
所以就直接找出的最大的,然后把更新为,同时即可
版本1如下
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,x,a[2000],ans; int getin() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar(); return x; } int main() { x=getin(),n=getin(); for(int i=1;i<=n;i++)a[i]=getin(); sort(a+1,a+n+1); if(a[1]!=1){cout<<-1;return 0;} int sum=0; while(sum<x) { int i; for(i=n;i>=1;i--)if(a[i]<=sum+1)break; ans++,sum+=a[i]; } cout<<ans<<endl; }复杂度,但是实际上一般跑不到这个上界
这样的效率对于这道题已经绰绰有余了,但是我们还是要想办法优化
注意到单调递增,所以可以用一个二分来找出最大值
复杂度的版本2
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,x,a[2000],ans; int getin() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar(); return x; } int find(int x) { int l=1,r=n,mid; while(l<r) { mid=(l+r+1)>>1; if(a[mid]<=x)l=mid; else r=mid-1; } return l; } int main() { x=getin(),n=getin(); for(int i=1;i<=n;i++)a[i]=getin(); sort(a+1,a+n+1); if(a[1]!=1){cout<<-1;return 0;} int sum=0; while(sum<x) { int i=find(sum+1); ans++,sum+=a[i]; } cout<<ans<<endl; }退回到版本1,注意到一个值可能会被重复累加,可不可以快速地找出累加次数呢?
显然是可以的
当时就会被代替
令最小可以解出
于是这样每次都可以使下标,所以复杂度
和版本2结合可以得到一个复杂度的做法
版本3
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,x,a[2000],ans; int getin() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar(); return x; } int find(int x) { int l=1,r=n,mid; while(l<r) { mid=(l+r+1)>>1; if(a[mid]<=x)l=mid; else r=mid-1; } return l; } int main() { x=getin(),n=getin(); for(int i=1;i<=n;i++)a[i]=getin(); a[n+1]=1e9;//注意这里要赋一个极大值避免出现问题 sort(a+1,a+n+1); if(a[1]!=1){cout<<-1;return 0;} int sum=0; while(sum<x) { int i=find(sum+1); int k=ceil((double)(min(x,a[i+1])-sum-1)/a[i]);//要和s取min ans+=k,sum+=a[i]*k; } cout<<ans<<endl; }版本4其实已经不难想到了
我们每次选取的i都是递增的,那么直接记录上次选取的i,复杂度
版本4
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int n,x,a[2000],ans; int getin() { int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')x=x*10+ch-48,ch=getchar(); return x; } int main() { x=getin(),n=getin(); for(int i=1;i<=n;i++)a[i]=getin(); a[n+1]=1e9; sort(a+1,a+n+1); if(a[1]!=1){cout<<-1;return 0;} int sum=0,i=0; while(sum<x) { while(a[i+1]<=sum+1)i++; int k=ceil((double)(min(x,a[i+1])-sum-1)/a[i]); ans+=k,sum+=a[i]*k; } cout<<ans<<endl; }PS:可能正常人都是直接跳到版本4的只有我这种蒟蒻才会想这么多
- 1
信息
- ID
- 650
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者