首页 > 试题广场 >

兑换零钱(一)

[编程题]兑换零钱(一)
  • 热度指数:78258 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
给定数组arr,arr中所有的值都为正整数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个aim,代表要找的钱数,求组成aim的最少货币数。
如果无解,请返回-1.

数据范围:数组大小满足 , 数组中每个数字都满足

要求:时间复杂度 ,空间复杂度

示例1

输入

[5,2,3],20

输出

4
示例2

输入

[5,2,3],0

输出

0
示例3

输入

[3,5],2

输出

-1

备注:
0 \leq n \leq 10\,000
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public int minMoney (int[] arr, int aim) {
        // write code here
        if(aim == 0){
            return 0;
        }
        // 思路:每一个dp[i]都代表该dp[i]能否被找零,arr数组里面的数,代表dp[数]一定是能被找零的,接下来往后推即可
        int dp[] = new int[aim+1];
        Arrays.fill(dp,1,dp.length,aim+1);
        for(int i=1; i<=aim; i++){
            for(int coin : arr){
                if(i-coin < 0 || dp[i-coin]>=aim) continue;
                dp[i] = Math.min(dp[i-coin]+1,dp[i]);
            }
        }
        return dp[aim] == aim +1  ? -1 : dp[aim];
    }
}


发表于 2022-08-22 15:30:28 回复(0)
class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        dp = [aim+1 for i in range(aim+1)]
        dp[0] = 0
        for i in range(1, aim+1):
            for j in arr:
                if i >= j:
                    dp[i] = min(dp[i], dp[i-j]+1)
        return dp[aim] if dp[aim] < aim+1 else -1
发表于 2022-05-22 14:34:12 回复(0)
class Solution {
public:
    int minMoney(vector<int>& arr, int aim) {
        vector<int>dp(aim+1,aim+1);dp[0]=0;
        for(int i=0;i<arr.size();i++)
        {
                for(int j=arr[i];j<=aim;j++)
                dp[j]=min(dp[j],dp[j-arr[i]]+1);
        }
        if(dp[aim]==aim+1)return -1;
        return dp[aim];
    }
};

发表于 2021-12-28 08:02:12 回复(0)
 def minMoney(self , arr , aim ):
        # write code here
        if not aim:
            return 0
        level =seen= {0}
        res = 0
        while level:
            if aim in level:
                return res
            level = {a+c for a in level for c in arr if a+c <= aim}-seen
            seen |= level
            res += 1
        
        return -1
发表于 2021-06-26 16:55:30 回复(0)
class Solution {
public:

	int minMoney(vector<int>& arr, int aim) {
		vector<int> dp(aim+1, 1e9);

		//拼出0元只需要0个硬币
		dp[0] = 0;

		//计算拼出1到aim最少需要多少张货币
		for (int i = 1; i <= aim; i++)
		{
			for (int j = 0; j < arr.size(); j++)
			{
				//从dp[0]开始,在之前的状态上叠加
				//计算dp[6],6-arr[0]=1,而dp[1]==1e9,跳过
				//6-arr[1]=4,而dp[4]==2(拼出4块钱最少需要两张2块),=》dp[6]=dp[4]+1=3
				//6-arr[2]=3,而dp[3]==1(拼出3块钱最少需要一张3块),=》dp[6]=min(3,dp[3]+1)=2
				if (i - arr[j] >= 0 && dp[i - arr[j]] != 1e9)
					dp[i] = min(dp[i], dp[i - arr[j]] + 1);
			}
		}
		if (dp[aim] == 1e9)
			return -1;
		return dp[aim];
	}
};

发表于 2021-06-05 21:21:12 回复(0)
import java.util.*;


public class Solution {
    /**
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public int minMoney (int[] arr, int aim) {
        // write code here
        if(arr == null || arr.length == 0)
            return -1;
        Arrays.sort(arr);
        int[] res = new int[aim + 1];
        res[0] = 0;
        for(int i = 1; i < aim + 1; ++ i){
            int min = Integer.MAX_VALUE;
            for(int j = 0; j < arr.length; ++ j){
                int n = i - arr[j];
                if(n < 0)
                    break;
                else{
                    if(res[n] == Integer.MAX_VALUE)
                        continue;
                    else
                        min = Math.min(min, res[n] + 1);
                }
            }
            res[i] = min;
        }
        return res[aim] == Integer.MAX_VALUE ? -1 : res[aim];
    }
}

发表于 2020-10-04 11:46:26 回复(0)
    public int minMoney(int[] arr, int aim) {
        //dp[i] 换得金额i能用的最少硬币数
        int[] dp = new int[aim + 1];
        //后面要比较最小值 所以每个dp的初始值都是aim+1 , 考虑硬币额度全为1用aim枚能换aim额度 aim+1必然是越界值了
        Arrays.fill(dp, aim + 1);
        dp[0] = 0; //因为要给dp[1-1]做铺垫 所以dp[0]必须是0
        for (int i = 1; i < aim + 1; i++) {
            for (int j = 0; j < arr.length; j++) {
                //别越界 && 至少能换出来才换 && 能换的话 看看我用这枚硬币好 还是不用好 
                // && 如果能用硬币你不用的话(或者压根换不出来) 那代价可是MAX值 逼着你尽可能换
                if (i - arr[j] >= 0) dp[i] = Math.min(dp[i], dp[i - arr[j]] + 1);
            }
        }
        //要是流程走下来 dp值是非法值 说明换不出来
        return dp[aim]==aim+1?-1:dp[aim];
    }

发表于 2021-03-22 18:43:09 回复(3)
占个楼

import java.util.*;


public class Solution {
    /**
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public static int minMoney (int[] arr, int aim) {
        int len = arr.length;
        //dp[i][j]的含义为:在可以任意使用arr[0...i]货币的情况下,组成j所需的最小张数。
        int dp[][] = new int[len][aim + 1];

        // 初始化
        for(int i = 0; i < len; i++){
            dp[i][0] = 0;
        }
        for(int j = 1; j <= aim; j++){
            dp[0][j] = Integer.MAX_VALUE;// 无法凑出数值为j的钱
            if(j-arr[0] >= 0 && dp[0][j-arr[0]] != Integer.MAX_VALUE){
                dp[0][j] = dp[0][j-arr[0]] + 1;// 仅使用第一种类型的货币
            }
        }

        // 更新
        for(int i = 1; i < len; i++){
            for(int j = 1; j <= aim; j++){
                if(j - arr[i] >= 0 && dp[i][j - arr[i]] != Integer.MAX_VALUE) {
                    // 判断不使用当前种类的货币和仅使用一张当前种类的货币这两种情况下,哪一种方案使用的货币少
                    dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - arr[i]] + 1);
                }else{
                    // 不使用当前种类的货币
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[len - 1][aim] == Integer.MAX_VALUE ? -1 : dp[len - 1][aim];
    }
}


编辑于 2020-09-29 10:01:29 回复(2)
    public int minMoney (int[] arr, int aim) {
        int[]dp=new int[aim+1];
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0]=0;//凑够0元不需要硬币
        for(int i=1;i<=aim;i++){//动态规划,i表示要凑够的钱数,从最小值开始计算
            for(int coin:arr){//对于每种货币,首先判断能否使用,即coin<=i
                if(i-coin>=0&&dp[i-coin]!=Integer.MAX_VALUE)//此外,得保证i-coin是可以凑成的
                    dp[i]=Math.min(dp[i],dp[i-coin]+1);
            }
        }
        return dp[aim]==Integer.MAX_VALUE?-1:dp[aim];//最后如果不能被修改,说明不能凑成
    }

发表于 2021-03-25 18:49:52 回复(0)
int minMoney(int* arr, int arrLen, int aim ) {
    // write code here
    int dp[aim+1];//初始化数组dp,长度aim+1,除了dp[0]=0其他都为-1
    for(int i=1;i<aim+1;i++){
        dp[i]=-1;
    }
    dp[0]=0;
    
    for(int i=1;i<=aim;i++){//从1开始遍历到aim,依次算1到aim的最优解
        for(int j=0;j<arrLen;j++){//设置变量j,遍历其中面值数组
            //如果i的值大于面值并且dp[i-arr[j]]有最优解
            if(arr[j]<=i&&dp[i-arr[j]]!=-1){
            //如果金额i还没有计算或者dp[i]比正在计算的dp[i-arr[j]]+1大
                if(dp[i]==-1||dp[i]>dp[i-arr[j]]+1)
                    //更新dp[i]
                    dp[i]=dp[i-arr[j]]+1;
            }
        }
    }
    return dp[aim];
   
}
发表于 2022-05-24 11:02:47 回复(1)
class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        dp = [aim+1]*(aim+1)
        dp[0]=0
        for i in range(1,aim+1):
            for j in arr:
                if j>i:
                    continue
                dp[i] = min(dp[i],dp[i-j]+1)
        return dp[aim] if dp[aim]!=aim+1 else -1

发表于 2023-09-26 18:39:55 回复(0)
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 最少货币数
     * @param arr int整型vector the array
     * @param aim int整型 the target
     * @return int整型
     */
    int minMoney(vector<int>& arr, int aim) {
        // write code here
        vector<int>V(aim+1,0);
        int i=0;
        for(i=1;i<=aim;i++)
        {
            for(int j=0;j<=arr.size();j++)
            {
                if(j==arr.size())
                {if(V[i]==0)
                    V[i]=-1;
                    break;}
                
                if(arr[j]>i)
                continue;
                else {
                    if(V[i]>(V[i-arr[j]]+1)||V[i]==0)
                    if(V[i-arr[j]]!=-1)
                    V[i]=V[i-arr[j]]+1;
                }
            }
        }
        return V[aim];
    }
};

发表于 2023-07-02 11:14:36 回复(0)
public class Solution {
    public int minMoney (int[] arr, int aim) {
        // 凑成i块钱所需要的最少货币数
        int[] dp = new int[aim + 1];
        Arrays.fill(dp, aim + 1);
        // 凑成0元需要0个货币数
        dp[0] = 0;
        // 先遍历货币
        for (int c : arr) {
            // 后根据货币数值遍历目标金额
            for (int i = c; i <= aim; i++) {
                // 将当前货币加入
                // 不将当前货币加入
                // 取个最小值
                dp[i] = Math.min(dp[i - c] + 1, dp[i]);
            }
        }
        // 如果目标金额的所需最少货币数是aim+1,说明凑不成aim
        return dp[aim] == aim + 1 ? -1 : dp[aim];
    }
}

public class Solution {
    public int minMoney (int[] arr, int aim) {
        // 凑成i块钱所需要的最少货币数
        int[] dp = new int[aim + 1];
        Arrays.fill(dp, aim + 1);
        // 凑成0元需要0个货币数
        dp[0] = 0;
        // 先遍历目标金额
        for (int i = 1; i <= aim; i++) {
            // 遍历货币
            for (int c : arr) {
                // 如果目标金额比货币值还小,直接跳过
                if (i < c) {
                    continue;
                }
                // 将当前货币加入
                // 不将当前货币加入
                // 取个最小值
                dp[i] = Math.min(dp[i - c] + 1, dp[i]);
            }
        }
        // 如果目标金额的所需最少货币数是aim+1,说明凑不成aim
        return dp[aim] == aim + 1 ? -1 : dp[aim];
    }
}

发表于 2023-06-04 16:38:35 回复(0)
class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        # 定义a[i]为组成i的最少货币数
        # 状态转移矩阵a[i] = min(a[i-arr[0]], a[i-arr[n]]) + 1
        # 输出值a[aim]
        # 边界条件: a[i]全部初始化为0即可

        a = [0] * (aim + 1)
        for i in range(1, aim+1):
            min_num = 9999
            for coin in arr:
                if i >= coin:
                    min_num = min(min_num, a[i-coin] + 1)
            a[i] = min_num
        return a[aim] if a[aim] < 9999 else -1

发表于 2023-03-03 10:20:19 回复(0)
class Solution {
public:
    /**
     * 最少货币数
     * @param arr int整型vector the array
     * @param aim int整型 the target
     * @return int整型
     */
    int minMoney(vector<int>& arr, int aim) {
        // 时间复杂度O(N*aim),空间复杂度O(aim)
        vector<int> dp(aim + 1, aim + 1);
        dp[0] = 0;
        for (int i = 1; i <= aim; ++i) {
            for (int j = 0; j < arr.size(); ++j) {
                if (i >= arr[j]) dp[i] = min(dp[i], dp[i - arr[j]] + 1);
            }
        }
        return dp[aim] == aim + 1 ? -1 : dp[aim];
    }
};

发表于 2022-11-08 11:28:06 回复(0)
脑溢血做法:递归+回溯
public int minMoney(int[] arr, int aim) {
        // write code here
        int len = arr.length;
        if (len == 0)
            return -1;
        if (aim == 0)
            return 0;
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        minMoneyRecursiveHelper(arr, aim, 0, minHeap, len - 1);

        return minHeap.size() == 0 ? -1 : minHeap.poll();

    }

    public void minMoneyRecursiveHelper(int[] arr, int aim, int minCnt, PriorityQueue<Integer> queue, int index) {
        if (aim == 0) {
            queue.offer(minCnt);
            return;
        }
        if (index < 0 || aim < 0 || (queue.size() > 0 && minCnt >= queue.peek())) {
            return;
        }
        // 先递归,到最深, 如 len -1 =5 时,从 index =1 ~ 5 依次计算
        minMoneyRecursiveHelper(arr, aim, minCnt, queue, index - 1);
        // 第一个递归弹栈时,index = 0,1,2...,类似于回溯
        minMoneyRecursiveHelper(arr, aim - arr[index], minCnt + 1, queue, index);
    }

动态规划数组解法:
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 最少货币数
     * @param arr int整型一维数组 the array
     * @param aim int整型 the target
     * @return int整型
     */
    public int minMoney(int[] arr, int aim) {
        // 如果目标金额为0,则不需要任何货币
        if (aim == 0) return 0;
        // dp[i] 表示凑成金额 i 所需的最少货币数
        int[] dp = new int[aim + 1];
        // 初始化为一个大数,代表无法凑成的情况
        Arrays.fill(dp, aim + 1);
        dp[0] = 0; // 凑成金额0的货币数为0

        for (int i = 1; i <= aim; i++) {
            for (int coin : arr) {
                if (i >= coin) {
                    // 当前的次数 = 除去当前货币的次数  + 当前货币这次
                    // 内部循环,会尝试所有的可能情况,找到最小值
                    // 如果 dp[i - coin], dp[i] 未被更新,则会更新一个较大值
                    // 因此整个组合过程只要有一个凑不成,就会更新较大值,就代表无法组合
                    dp[i] = Math.min(dp[i - coin] + 1, dp[i]);
                }
            }
        }

        // 如果dp[aim]为初始值,则表示无法凑成
        return dp[aim] > aim ? -1 : dp[aim];
    }
}


编辑于 2024-04-07 12:15:15 回复(0)
#include <algorithm>
#include <cstdio>
#include <functional>
#include <iterator>
#include <memory>
#include <vector>
class Solution {
  public:
      int minMoney(vector<int>& arr, int aim) {
        if(aim==0){
        return 0;    
        }
        if(arr.size()==0){
            return -1;
        }
        vector<int>d(aim+1,aim+1);
        d[0]=0;
        for(int i=1;i<=aim;i++){
            for(int j=0;j<arr.size();j++){
                if(i>=arr[j]){
                    d[i]=min(d[i],d[i-arr[j]]+1);
                }
            }
        }
        return d[aim]>aim?-1:d[aim];
    }
};

编辑于 2024-04-05 12:51:59 回复(0)
脑筋急转弯,根本不考编程。
int minMoney(int* arr, int arrLen, int aim ) {
    int dp[5001], i, j, res;
    if(arrLen==0)
        return -1;
    if(aim==0)
        return 0;
    for(i=0; i<aim; i++) 
        dp[i] = aim+1;
    for(i=0; i<arrLen; i++) 
        dp[arr[i]-1] = 1;
    for(i=0; i<aim; i++) 
        for(j=0; j<arrLen; j++) 
            if(i>=arr[j]) 
                dp[i] = min(dp[i], dp[i-arr[j]]+1);
    return (dp[aim-1]<=aim ? dp[aim-1] : -1);
}

编辑于 2024-03-25 20:46:03 回复(0)
参考大佬的代码,写一下自己的理解,可能会有帮助:
//dp[i-arr[j]]+1表示:dp[i]扣除遇到的这张单元面值arr[j]之后,剩下的钱数最少能用dp[i-arr[j]]来表示。+1即代表加上当前遇到的单元面值。
/*比如,货币的单元面值[2,3,5];
dp[0]=0;
dp[1]=max;
dp[2]=1;
dp[3]=1;
dp[4]=2;
dp[5]=2;
求dp[6],首先dp[6-arr[0]]==dp[6-2]==dp[4]==2,表示6元如果扣除2元后剩下的4元面值是可以用最少2张货币来表示的,而除去这两张之外,还要算上2元这一张面值才构成了整个6元。此时一共3张,小于max,因此dp[6]更新为3;
下一轮循环,dp[6-arr[1]]==dp[6-3]==dp[3]==1,表示6元扣除3元后剩下的3元可以从之前的结果中查到最少需要一张货币,1张+1,一共2张,小于当前存储的3张,因此dp[6]更新为2;
下一轮循环,dp[6-arr[2]]==dp[6-5]==dp[1]==max>2,说明无法更新,最小值仍是2,此时循环结束,跳出内层循环,求得dp[6]==2.
*/


发表于 2024-03-23 23:32:55 回复(0)

这个问题是一个典型的动态规划问题,也被称为“完全背包问题”。我们可以使用动态规划来求解组成给定钱数aim所需的最少货币数。

首先,我们定义一个数组dp,其中dp[i]表示组成钱数i所需的最少货币数。初始时,我们将dp数组的所有元素都设置为一个较大的数(比如aim + 1),表示初始状态下无法组成任何钱数。然后,我们将dp[0]设置为0,表示组成钱数0不需要任何货币。

接下来,我们遍历数组arr中的每个货币面值,对于每个面值,我们再次遍历dp数组,从该面值开始,更新每个钱数所需的最少货币数。具体的更新方式是:如果当前钱数i可以由之前的某个钱数j(j < i)加上当前面值得到,并且使用当前面值组成钱数i所需的货币数比之前的更少,那么我们就更新dp[i]。

最后,我们检查dp[aim]的值,如果它仍然是我们初始设置的那个较大的数,说明无法组成钱数aim,返回-1;否则,返回dp[aim]作为结果。

class Solution:
    def minMoney(self , arr: List[int], aim: int) -> int:
        # write code here
        dp = [aim + 1] * (aim + 1)  
        dp[0] = 0  

        for coin in arr:  
            for i in range(coin, aim + 1):  
                dp[i] = min(dp[i], dp[i - coin] + 1)  

        return dp[aim] if dp[aim] != aim + 1 else -1
发表于 2024-03-15 23:07:39 回复(0)