12 换钱的最少货币数
Table of Contents

ac

动态规划并使用空间压缩

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    public static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {

        int n = sc.nextInt();
        int aim = sc.nextInt();
        int[] arr = new int[n];
        for(int i=0;i<n;i++){
            arr[i] = sc.nextInt();
        }
        if(aim < 0){
            System.out.print(-1);
            return;
        }
        int maxVal = Integer.MAX_VALUE;
        /**
         * dp[i][j],arr[0...i]换取钱数为j的最小数目
         * dp[][0] = 0, 换取钱数为0的数目都设置成0;若换取钱数换不到可以设成 Integer.MAX_VALUE
         * 二维空间压缩
         */
        int[] dp = new int[aim+1];
        dp[0] = 0;
        for(int j =1;j<=aim;j++){
            dp[j] = maxVal;
        }
        int val = arr[0];
        int cnt = 1;
        while (val <= aim){
            dp[val] = cnt;
            cnt++;
            val = cnt * arr[0];
        }
        // 动归
        for(int i=1;i<n;i++){
            // 完全不使用 arr[i]
            // 到使用1张 arr[i]
            // 使用2张 arr[i]
            // ...
            // 使用 aim / arr[i] 张
            for(int j=1;j<=aim;j++) {
                int tmpDpJ = maxVal;
                int cntMax = j / arr[i];
                for (int k = 0; k <= cntMax; k++) {
                    int tmp = dp[j - k * arr[i]] + k;
                    if(tmp >= 0 && tmp <= tmpDpJ) {
                        tmpDpJ = tmp;
                    }
                }
                dp[j] = tmpDpJ;
            }
        }
        if(dp[aim] == Integer.MAX_VALUE) {
            System.out.println(-1);
        }else {
            System.out.println(dp[aim]);
        }
    }

}

附:二维可存储构造状态的动态规划

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {

    public static Scanner sc = new Scanner(System.in);

    /*
3 20
5 2 3

4

2 2
3 5

-1
     */
    public static void main(String[] args) {

        int n = sc.nextInt();
        int aim = sc.nextInt();
        int[] arr = new int[n];
        for(int i=0;i<n;i++){
            arr[i] = sc.nextInt();
        }
        int maxVal = Integer.MAX_VALUE;
        /**
         * dp[i][j],arr[0...i]换取钱数为j的最小数目
         * dp[][0] = 0, 换取钱数为0的数目都设置成0;若换取钱数换不到可以设成 Integer.MAX_VALUE
         */
        int[][] dp = new int[n][aim+1];
        // j = 0 都设置为0
        for(int i = 0; i < n; i++){
            dp[i][0] = 0;
        }
        // 只使用第一张
        // 处理dp[0][1...aim]
        for(int j =1;j<=aim;j++){
            dp[0][j] = maxVal;
        }
        int val = arr[0];
        int cnt = 1;
        while (val <= aim){
            dp[0][val] = cnt;
            cnt++;
            val = cnt * arr[0];
        }
        // 动归
        for(int i=1;i<n;i++){
            // 完全不使用 arr[i]
            // 到使用1张 arr[i]
            // 使用2张 arr[i]
            // ...
            // 使用 aim / arr[i] 张
            for(int j=1;j<=aim;j++) {
                dp[i][j] = maxVal;
                int cntMax = j / arr[i];
                for (int k = 0; k <= cntMax; k++) {
                    int tmp = dp[i - 1][j - k * arr[i]] + k;
                    if(tmp >=0 && tmp <= dp[i][j]) {
                        dp[i][j] = tmp;
                    }
                }
            }
        }
        if(dp[n-1][aim] == Integer.MAX_VALUE) {
            System.out.println(-1);
        }else {
            System.out.println(dp[n-1][aim]);
        }
    }

}