P1658 购物(贪心算法)

P1658 购物
提交 2.48k
通过 1.16k
时间限制 1.00s
内存限制 125.00MB
题目描述

你就要去购物了,现在你手上有N种不同面值的硬币,每种硬币有无限多个。为了方便购物,你希望带尽量少的硬币,但要能组合出1到X之间的任意值。
输入格式

第一行两个数X、N,以下N个数,表示每种硬币的面值。

【数据规模】

对于30%的数据,满足N≤3,X≤20;

对于100%的数据,满足N≤10,X≤1000.
输出格式

最少需要携带的硬币个数,如果无解输出-1.
输入输出样例
输入 #1

20 4

1 2 5 10

输出 #1

5
题目链接:https://www.luogu.com.cn/problem/P1658
1.首先我们要判断数组内是否存在1,因为面额1只有1可以组成,而且如果存在面值1的硬币,是一定可以组成出1-x的面值。
2.每次尽量用最大面值硬币组合,这样子才可以组成出最小的携带数。因此用sort排序。
上样例分析:

第一次取1:
reach=1;当前可以组成的面额值
第二次取2:1,2,3
观察:3是利用1+2得出的,如果我们当前可以取到的面额为reach,那么我们再携带的一个硬币a[i],1–reach+a[i]都是可以组合出来的,因为2可以和1–reach的面值任意组合都是可行的所以reach更新为reach+=a[i];reach为3
第三次取2:1,2,3,4,5
与上同理reach为5

#include<iostream>
#include<cstdio>
#include<algorithm>
#define INF 0x3f3f3f3f
#define ll long long
#define speed(x) ios::sync_with_stdio(false), cin.tie(x), cout.tie(x)
#define bug(x) cout << #x << " == " << x << '\n';
const ll int MAX_N = 5e5 + 5;
using namespace std;
const int Maxn=1005;
int a[MAX_N]={0};
int main()
{
   int n,x;
   cin>>x>>n;

   for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);

    sort(a+1,a+n+1);

    if(a[1]!=1)//需要可以组合出1-x的面值,而面值1只有1本身可以组合,
    {
        cout<<-1<<'\n';//而且如果有面值1无论后面的面值是怎么样的,都可以组合出1-x的面值例:1 1 1 1 1 .....
        return 0;
    }
    int reach=0;//可以组成的面额值
    int ans=0;
    while(reach<x)
    {
        int i=n;
        for(;i>=1;i--)//尽量找最大的
        {
            if(a[i]<=reach+1)
                break;
        }//1~reach面额的钱都可以组成,所以只需要找最大面额的钞票,
        //该钞票可以和reach+1-a[i]的钱组成reach+1或者直接等于reach+1
        reach+=a[i];//a[i]可以和reach+1-a[i]组成reach+1,同理a[i]可以和reach+2-a[i](reach+2-a[i]<=reach即可)组成reach+2....
        ans++;//携带的钞票增加1个;
    }
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值