最小的硬币找零(有限的供应)和更好的时间复杂度讨论

时间:2019-05-20 14:27:11

标签: algorithm recursion optimization data-structures dynamic-programming

该问题希望用户返回一个最小硬币列表作为找零。例如[.01,.10,.25],.40。并且(所有硬币都有10个补给)应返回[.10,.10,.10,.10],但不返回[.25,.1,.01,.01,.01,.01,.01] < / p>

贪婪的方法行不通。此问题是动态编程问题。所描述的解决方案是O(2 ^ n)。自底向上方法如何将其优化为O(n ^ 2)或更佳?


class CoinChange {


  public static List<Double> findMinRefundCombination(List<Double> inputCoins, double refundToMake) {
      List<Double> minCoins = new ArrayList<>();
      List<Double> coinsAccumulatedSoFar = new ArrayList<>();
      double refundSoFar = 0.0d;
      findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins,coinsAccumulatedSoFar, 0, refundSoFar);
      System.out.println(minCoins.size());
      return minCoins;
  }


  public static void findMinRefundCombinationHelper(List<Double> inputCoins, double refundToMake, List<Double> minCoins, List<Double> coinsAccumulatedSoFar, int curIndex, double refundSoFar) {

    if(refundSoFar > refundToMake || curIndex == inputCoins.size()) {
      return;
    }

    if(refundSoFar == refundToMake) {
      if(minCoins.isEmpty()) {
        for(Double coin: coinsAccumulatedSoFar)
          minCoins.add(coin);
      } else {
         if(coinsAccumulatedSoFar.size() < minCoins.size()) {
           minCoins.clear();
           for(Double coin: coinsAccumulatedSoFar)
              minCoins.add(coin);
         } 
      }
    }


    coinsAccumulatedSoFar.add(inputCoins.get(curIndex));
 //   findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar,curIndex,refundSoFar + inputCoins.get(curIndex));    
   findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar, curIndex + 1, refundSoFar + inputCoins.get(curIndex)); 
    coinsAccumulatedSoFar.remove(coinsAccumulatedSoFar.size() - 1);
    findMinRefundCombinationHelper(inputCoins, refundToMake, minCoins, coinsAccumulatedSoFar, curIndex + 1, refundSoFar);
  }

  public static void main(String[] args) {
    List<Double> inputCoins = new ArrayList<>();
    inputCoins.add(.01);
    // inputCoins.add();
    inputCoins.add(.10);
    inputCoins.add(.25);
    inputCoins.add(0.50);
    inputCoins.add(1.0);
    double refundToMake = 0.40;
    List<Double> minCoins = findMinRefundCombination(inputCoins, refundToMake);
    for(Double coin: minCoins) 
      System.out.print(coin + " ");
    System.out.println();
  }
}

1 个答案:

答案 0 :(得分:1)

如果您要表示的数量足够少,则此问题可以转换为背负式背包。

在注释中,您声明所有数字的精度是两位小数,因此所有数字都可以通过将它们乘以100来转换为整数。让我们从原始输入中给定的每个硬币中创建10个硬币,然后声明我们最多可以使用每个新硬币一次。

这里的想法类似于背包(Knapsack):让我们介绍一个函数F(k, i),该函数表示如果我们仅使用前几个k硬币,则需要多少硬币来实现总和i。例如,F(0, i)为0,因为对于我们可用的任何硬币子集,我们都可以不使用它们而获得总和0。 F(k > 0, 0)是未定义的,因为我们不能不使用硬币就无法获得大于0的总和,并且F(|value of the first coin|, 1)等于1。请注意,F(k, 10N)将是解决问题的方法。

对于F(k, i) = min(F(k, i - 1), F(k - |value of coin i|, i - 1))的适用值,此处的递归关系为k, i。用英语来说,我们是在说“要么我们使用第i个硬币,在这种情况下,总和必须增加其价值,否则我们就没有”。