贪心--需要添加的硬币的最小数量

题目描述

给你一个下标从 开始的整数数组 coins,表示可用的硬币的面值,以及一个整数 target 。

如果存在某个 coins 的子序列总和为 x,那么整数 x 就是一个 可取得的金额 

返回需要添加到数组中的 任意面值 硬币的 最小数量 ,使范围 [1, target] 内的每个整数都属于 可取得的金额 。

数组的 子序列 是通过删除原始数组的一些(可能不删除)元素而形成的新的 非空 数组,删除过程不会改变剩余元素的相对位置。

示例 1:

输入:coins = [1,4,10], target = 19
输出:2
解释:需要添加面值为 2 和 8 的硬币各一枚,得到硬币数组 [1,2,4,8,10] 。
可以证明从 1 到 19 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 2 。

示例 2:

输入:coins = [1,4,10,5,7,19], target = 19
输出:1
解释:只需要添加一枚面值为 2 的硬币,得到硬币数组 [1,2,4,5,7,10,19] 。
可以证明从 1 到 19 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 1 。

示例 3:

输入:coins = [1,1,1], target = 20
输出:3
解释:
需要添加面值为 4 、8 和 16 的硬币各一枚,得到硬币数组 [1,1,1,4,8,16] 。 
可以证明从 1 到 20 的所有整数都可由数组中的硬币组合得到,且需要添加到数组中的硬币数目最小为 3 。

提示:

  • 1 <= target <= 10^5
  • 1 <= coins.length <= 10^5
  • 1 <= coins[i] <= target

1. 思路

为方便处理,首先将数组 coins 按升序排序(由于子序列可以自由删除某些元素,而不改变相对位置,而我们这里只需要知道子数组的和就行,相对位置在本题中并无用处,如在数组{1,4,2,5}中取出{1,2,5}子序列和为1+2+5,而{5,2,1}的和也为8,因此可以进行排序),然后计算需要添加的硬币的最小数量。

对于正整数 x,如果区间 [1,x−1] 内的所有金额都可取得,且 x 在数组中,则区间 [1,2x−1](注意2x+1=x-1+x) 内的所有金额也都可取得。证明如下。

对于任意 1≤y<x,y 可取得,x 在数组中,因此 y+x 也可取得,区间 [x+1,2x−1](即区间 [1,x−1] 内的每个金额加上 x 之后得到的区间)内的所有金额也可取得,由此可得区间 [1,2x−1] 内的所有金额都可取得。

假设金额 x 不可取得,则至少需要在数组中添加一个小于或等于 x 的数,才能取得 x,否则无法取得 x。

如果区间 [1,x−1] 内的所有金额都可取得,则从贪心的角度考虑,添加 x 之后即可取得 x,且满足添加的金额个数最少。在添加 x 之后,区间 [1,2x−1] 内的所有金额都可取得,下一个不可取得的金额一定不会小于 2x。

由此可以提出一个贪心的方案。每次找到不可取得的最小的金额 x,在数组中添加 x,然后寻找下一个不可取得的最小的整数,重复上述步骤直到区间 [1,target] 中的所有金额都可取得。

具体实现方面,任何时候都应满足区间 [1,x−1] 内的所有金额都可取得。令 x 的初始值为 1,数组下标 index 的初始值为 0,则初始状态下区间 [1,x−1] 为空区间,满足区间内的所有金额都可取得。进行如下操作:

  • 如果 index在数组 coins 的下标范围内且 coins[index]≤x,则将 coins[index] 的值加给 x,并将 index 的值加 1。可取得的区间从 [1,x−1] 扩展到 [1,x+coins[index]−1],对 x 的值更新以后,可取得的区间为 [1,x−1]。
  • 否则,x 没有可取得,因此需要在数组中添加 x,然后将 x 的值乘以 2。在数组中添加 x 之后,可取得的区间从 [1,x−1] 扩展到 [1,2x−1],对 x 的值更新以后,可取得的区间为 [1,x−1][1,x-1]。
  • 重复上述操作,直到 x 的值大于 target。

由于任何时候都满足区间 [1,x−1] 内的所有金额都可取得,因此上述操作可以保证区间 [1,target] 内的所有金额都可取得。

又由于上述操作只在 x 不可取得时才在数组中添加 x,如果不添加 x 则 x 无法可取得,因此可以保证添加的金额个数最少。如果减少添加的金额个数,则无法取得区间 [1,target] 内的所有金额。

 2. 代码

public int minimumAddedCoins(int[] coins, int target) {
        Arrays.sort(coins);
        int x=1;
        int index=0;
        int ans=0;
        while (x<=target){
            if(index<coins.length && coins[index]<=x){
                x+=coins[index];
                index++;
            }else {
                x*=2;
                ans++;
            }
        }
        return ans;
    }

3. 参考题目

. - 力扣(LeetCode)

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值