换钱的最少货币数

题目描述

给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。

思路

动态规划。
使用dp数组,dp[i][j] 表示使用arr[0…i]的货币凑出j的面值需要的最少货币数。
根据最后的arr[i]是否使用进行讨论:

  1. 面值为arr[i]的货币不使用,此时dp[i][j] = dp[i-1][j]。
  2. 面值为arr[i]的货币使用,此时dp[i][j] = dp[i][j-arr[i]]+1,dp[i][j-arr[i]]表示使用arr[0…i]中的货币凑出面值j-arr[i]所需要的最少货币数,相当于在dp[i][j]中去掉一张面值为arr[i]的货币,所以结果需要加1,表示加上去掉的货币。
    因为题目是求最少货币数,所以应该取两种情况的最小值。即dp[i][j] =min(dp[i-1][j], dp[i][j-arr[i]] + 1);

根据以上递推公式可以得出一个dp值只依赖于其上方和左侧的两个值,所以先初始化dp数组的第一行和第一列,再进行迭代求解,求出的dp[n-1][aim](dp数组右下角的值)就是答案。

时间复杂度为求整个dp数组的时间复杂度,即O(n*aim)。

注意的问题

有一种情况是使用当前的货币无论如何也凑不出给定的面值,比如[2,4]凑出面值为5。此时应该给当前的dp值一个特殊的标记,比如-1。有特殊标记的d p值不应该参与min的比较。

代码实现

import java.util.Scanner;
public class Main{
    public static void main(String []args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int aim = scanner.nextInt();
        int []arr = new int[n];
        for(int i = 0 ; i<n ; i++){
            arr[i] = scanner.nextInt();
        }
        long [][]dp = new long[n][aim+1];
        //dp[i][j]:使用aim[0...i]的货币组成j的最少货币数
        //答案:dp[n-1][aim];
        //dp[i][j] = min(dp[i-1][j] , dp[i][j-arr[i]]+ 1);
        for(int j = 1 ; j<=aim; j++){
            dp[0][j] = (j%arr[0] == 0)? j/arr[0]:-1;
        }
        for(int i = 0 ; i<n ; i++){
            dp[i][0] = 0;
        }
        for(int i = 1; i<n ; i++){
            for(int j = 1 ; j<=aim ; j++){
                dp[i][j] = dp[i-1][j];
                if(j-arr[i] >= 0){
                    if(dp[i][j-arr[i]] != -1 && dp[i][j] != -1){
                        dp[i][j] = Math.min(dp[i][j], dp[i][j-arr[i]]+1);
                    }else if(dp[i][j-arr[i]]== -1 && dp[i][j] == -1){
                        dp[i][j] = -1;
                    }else{
                        dp[i][j] = (dp[i][j] == -1)? dp[i][j-arr[i]]+1:dp[i][j];
                    }
                }
            }
        }
        System.out.println(dp[n-1][aim]);
    }
}

你可能感兴趣的:(数据结构与算法,动态规划,数据结构,算法,leetcode)