leetcode
leetcode 2501 ~ 2550
需要添加的硬币的最小数量

需要添加的硬币的最小数量

难度:

标签:

题目描述

You are given a 0-indexed integer array coins, representing the values of the coins available, and an integer target.

An integer x is obtainable if there exists a subsequence of coins that sums to x.

Return the minimum number of coins of any value that need to be added to the array so that every integer in the range [1, target] is obtainable.

A subsequence of an array is a new non-empty array that is formed from the original array by deleting some (possibly none) of the elements without disturbing the relative positions of the remaining elements.

 

Example 1:

Input: coins = [1,4,10], target = 19
Output: 2
Explanation: We need to add coins 2 and 8. The resulting array will be [1,2,4,8,10].
It can be shown that all integers from 1 to 19 are obtainable from the resulting array, and that 2 is the minimum number of coins that need to be added to the array. 

Example 2:

Input: coins = [1,4,10,5,7,19], target = 19
Output: 1
Explanation: We only need to add the coin 2. The resulting array will be [1,2,4,5,7,10,19].
It can be shown that all integers from 1 to 19 are obtainable from the resulting array, and that 1 is the minimum number of coins that need to be added to the array. 

Example 3:

Input: coins = [1,1,1], target = 20
Output: 3
Explanation: We need to add coins 4, 8, and 16. The resulting array will be [1,1,1,4,8,16].
It can be shown that all integers from 1 to 20 are obtainable from the resulting array, and that 3 is the minimum number of coins that need to be added to the array.

 

Constraints:

  • 1 <= target <= 105
  • 1 <= coins.length <= 105
  • 1 <= coins[i] <= target

代码结果

运行时间: 92 ms, 内存: 26.4 MB


/*
 * Problem statement: Same as above.
 *
 * Solution approach using Java Streams:
 * 1. Sort the coins using streams.
 * 2. Iterate over the sorted list to calculate the smallest missing sum and required additions.
 * 3. Return the number of coins added.
 */

import java.util.Arrays;

public class MinCoinsStream {
    public static int minAdditions(int[] coins, int target) {
        // Sort the array using streams
        coins = Arrays.stream(coins).sorted().toArray();
        long missingSum = 1; // Start with the smallest missing value
        int addedCoins = 0;  // Number of coins added

        for (int coin : coins) {
            if (missingSum > target) break;
            if (coin <= missingSum) {
                missingSum += coin;
            } else {
                while (missingSum < coin && missingSum <= target) {
                    missingSum += missingSum;
                    addedCoins++;
                }
                missingSum += coin;
            }
        }

        while (missingSum <= target) {
            missingSum += missingSum;
            addedCoins++;
        }

        return addedCoins;
    }

    public static void main(String[] args) {
        int[] coins = {1, 4, 10};
        int target = 19;
        System.out.println(minAdditions(coins, target)); // Output: 2
    }
}

解释

方法:

该题解使用了贪心算法来确定最少需要添加的硬币数量以覆盖从1到target的所有金额。首先对硬币进行排序,然后用两个指标来跟踪:right表示当前可以通过已有硬币和新加入的硬币连续覆盖的最大值,i是遍历硬币数组的索引。在每次迭代中,如果当前硬币值大于right+1(表示有缺口无法用现有的硬币连续覆盖到该硬币的值),则添加一个硬币,其面值为right+1,以此填补缺口并尝试增大覆盖范围。这样保持覆盖范围的连续性,直到覆盖到target。最后,返回添加的硬币数量。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在遍历硬币值时,如果遇到当前硬币值大于right+1,你选择添加面值为right+1的硬币而不是其他值?
选择添加面值为right+1的硬币是因为,此时right+1是当前无法用已有硬币组合达到的最小金额。为了确保可以连续覆盖所有的金额直到target,必须首先填补这个最小的缺口。添加面值为right+1的硬币能够最有效地扩展已覆盖的金额范围,因此这是贪心策略中最优的选择,保证了每次操作都尽可能最大地增加覆盖范围。
🦆
在该算法中,当所有给定的硬币都已使用完毕,而right仍未达到target时,你是如何确定接下来需要添加的硬币数和面值的?
当所有给定的硬币都已使用完毕,而right仍未达到target时,算法继续按照之前的策略添加硬币。即继续添加面值为right+1的硬币。这样做是因为每次添加面值为right+1的硬币仍然是填补当前金额连续性的最有效方式。通过不断添加这样的硬币,直到覆盖范围扩展到或超过target。
🦆
该贪心策略是否保证了最小数量的硬币添加?存在哪些情况可能导致需要添加的硬币数目不是最优解?
该贪心策略在多数情况下能够保证最小数量的硬币添加,但并不总是保证全局最优解。特别是在某些特殊组合的硬币面值时,可能存在更优的硬币组合策略。例如,如果硬币面值大而稀疏,且target相对较大,仅依靠贪心策略可能无法发现某些利用大面值硬币通过较少数量的添加来实现覆盖的策略。在这种情况下,可能需要更复杂的动态规划方法来找到真正的最小添加数量。
🦆
在执行while循环时,对于right每次增加right + 1的策略为何能有效覆盖到target,这里的数学原理是什么?
这里的数学原理基于从当前已覆盖的最大金额right出发,通过添加right+1的硬币,可以确保金额的连续覆盖范围扩展到right + (right + 1)。这意味着每次操作后,覆盖的金额范围几乎翻倍。这种策略利用了连续性和累加的特性,保证了每次添加硬币后,新的可覆盖金额范围是前一范围的扩展,从而有效地逼近目标金额target。

相关问题