logologo

返回最少货币数

Nov 17, 2023

arr 是货币数组,其中的值都是正数,再给定一个正数 aim. 每个值都被认为是一张货币 返回组成 aim 的最少货币数 注意 📢:因为要求最少货币数组,所以每一张货币认为是相同或不同就不重要了

暴力递归


public static int minCoins(int[] arr, int aim) {
  return process(arr, 0, aim);
}

private static int process(int[] arr, int index, int rest) {
  if (rest < 0) return Integer.MAX_VALUE;
  if (index == arr.length) return rest == 0 ? 0 : Integer.MAX_VALUE;

  int p1 = process(arr, index + 1, rest);
  int p2 = process(arr, index + 1, rest - arr[index]);

  if (p2 != Integer.MAX_VALUE) p2++;
  return Math.min(p1, p2);
}

DP(记忆化搜索)

  public static int dp(int[] arr, int aim) {
  if (aim == 0) return 0;
  int N = arr.length;
  int[][] dp = new int[N + 1][aim + 1];
  dp[N][0] = 0;
  for (int j = 1; j < aim + 1; j++) {
    dp[N][j] = Integer.MAX_VALUE;
  }
  for (int index = N - 1; index >= 0; index--) {
    for (int rest = 0; rest <= aim; rest++) {
      int p1 = dp[index + 1][rest];
      int p2 = rest - arr[index] >= 0 ? dp[index + 1][rest - arr[index]] : Integer.MAX_VALUE;
      if (p2 != Integer.MAX_VALUE)
        p2++;
      dp[index][rest] = Math.min(p1, p2);
    }
  }
  return dp[0][aim];
}

DP 优化(斜率优化)

public static class Info {
  public int[] coins;
  public int[] zhangs;

  public Info(int[] c, int[] z) {
    this.coins = c;
    this.zhangs = z;
  }
}

public static Info getInfo(int[] arr) {
  HashMap<Integer, Integer> counts = new HashMap<>();
  for (int value : arr) {
    if (!counts.containsKey(value))
      counts.put(value, 1);
    else
      counts.put(value, counts.get(value) + 1);
  }

  int N = counts.size();
  int[] coins = new int[N];
  int[] zhangs = new int[N];
  int index = 0;
  for (Entry<Integer, Integer> entry : counts.entrySet()) {
    coins[index] = entry.getKey();
    zhangs[index++] = entry.getValue();
  }
  return new Info(coins, zhangs);
}

/**
 * 斜率优化的DP
 *
 * @param arr
 * @param aim
 * @return
 */
public static int dp2(int[] arr, int aim) {
  if (aim == 0) return 0;

  Info info = getInfo(arr);
  int[] coins = info.coins;
  int[] zhangs = info.zhangs;

  int N = coins.length;
  int[][] dp = new int[N + 1][aim + 1];
  dp[N][0] = 0;
  for (int j = 1; j < aim + 1; j++) {
    dp[N][j] = Integer.MAX_VALUE;
  }

  for (int index = N - 1; index >= 0; index--) {
    for (int rest = 0; rest <= aim; rest++) {
      dp[index][rest] = dp[index + 1][rest];
      for (int zhang = 1; zhang * coins[index] <= aim && zhang <= zhangs[index]; zhang++) {
        if (rest - zhang * coins[index] >= 0 && dp[index + 1][rest - zhang * coins[index]] != Integer.MAX_VALUE) {
          dp[index][rest] = Math.min(dp[index][rest], zhang + dp[index + 1][rest - zhang * coins[index]]);
        }
      }
    }
  }
  return dp[0][aim];
}

DP 优化(基于滑动窗口的更新最小值结构)

public static int dp3(int[] arr, int aim) {
  if (aim == 0) return 0;

  Info info = getInfo(arr);
  int[] coins = info.coins;
  int[] zhangs = info.zhangs;

  int N = coins.length;
  int[][] dp = new int[N + 1][aim + 1];
  dp[N][0] = 0;
  for (int j = 1; j < aim + 1; j++) {
    dp[N][j] = Integer.MAX_VALUE;
  }

  for (int index = N - 1; index >= 0; index--) {
    for (int mod = 0; mod < Math.min(aim + 1, coins[index]); mod++) {
      LinkedList<Integer> window = new LinkedList<>();
      window.add(mod);
      dp[index][mod] = dp[index + 1][mod];
      for (int R = mod + coins[index]; R <= aim; R += coins[index]) {
        while (!window.isEmpty() && (dp[index + 1][window.peekLast()] == Integer.MAX_VALUE
            || dp[index + 1][window.peekLast()]
                + compensate(window.peekLast(), R, coins[index]) >= dp[index + 1][R])) {
          window.pollLast();
        }

        window.addLast(R);
        int overdue = R - coins[index] * (zhangs[index] + 1);
        if (window.peekFirst() == overdue)
          window.pollFirst();

        if (dp[index + 1][window.peekFirst()] == Integer.MAX_VALUE) {
          dp[index][R] = Integer.MAX_VALUE;
        } else {
          dp[index][R] = dp[index + 1][window.peekFirst()] + compensate(window.peekFirst(), R, coins[index]);
        }
      }
    }
  }
  return dp[0][aim];
}

public static int compensate(int pre, int cur, int coin) {
  return (cur - pre) / coin;
}

Java
浙ICP备2021022773号    2022-PRESENT © ZhengKe