完全背包变形
dp[i][v]: 用前i个硬币组合出价值v所需的最少硬币数
dp[0][0]=1, dp[0][v]=0 for v>0.
dp[i][v] = Min {
  (a) dp[i-1][v],      // 不用i硬币
  (b) dp[i][v-arr[i]]  // 用i硬币
}

完全与01背包的核心区别是
  - 01背包不能dirty write (一个硬币只能使用0或1次), 
    i.e. (b)选项为 dp[i-1][v-arr[i]]
  - 完全背包就是要dirty write (一个硬币可以使用多次)
    i.e. (b)选项为 dp[i][v-arr[i]]
import java.util.*;

public class Solution {
  
    // 完全背包变形
    public int minMoney (int[] arr, int aim) {
      int[] mem = new int[aim+1];
      // 这里如果用Integer.MAX_VALUE,15行会overflow.
      // 因为 aim <= 5000, 所以结果不可能大于5000.
      Arrays.fill(mem, 5001);
      mem[0] = 0;  // {0, 5001, 5001 ... ... 5001}
      
      for (int i = 0; i < arr.length; i++) {
        for (int x = arr[i]; x <= aim; x++) {
          mem[x] = Math.min(mem[x], mem[x-arr[i]]+1);
        }
      }
      
      return mem[aim] == 5001 ? -1 : mem[aim];
    }
}