递归和动态规划----换钱的最少货币数

关键点:

设 N = arr.length   dp[N][aim+1]  : dp[ i ][ j ]表示在可以任意使用arr[ 0...i ]货币的情况下,组成 j 所需的最小张数

  1. 第一行 : dp[0][0..j..aim] 表示只使用arr[0]货币的情况下,换钱数为 j 的最小张数;
  2. 第一列 : dp[0.. i .. N-1 ][0] 表示要换的钱数为 0 时需要的最小张数  dp[0..i..N][0] = 0;
  3. dp[ i ][ j ] 的值的可能取值:
  • 完全不使用arr[ i ]货币 : 即dp[ i - 1 ][ j ] 
  • 使用 k 张arr[ i ]货币 : 即 dp[ i ][ j - k*arr[i] ] + k   (k  >= 1)

 dp[ i ][ j ] = min{ dp[ i - 1 ][ j ], dp[ i ][ j - k*arr[i] ] + k }

==>   dp[ i ][ j ] = min{ dp[ i - 1 ][ j ], min{ dp[ i - 1 ][ j - x*arr[i] ] + x} }

==>   .......

==> dp[ i ][ j ] = min{ dp[ i - 1 ][ j ], dp[ i ][ j - arr[i] ] + 1 } 可以理解为完全没使用arr[ i ] 和 至少使用一张 arr[ i ]

代码: 

public static int minCoins(int[] arr,int aim) {
		if(arr == null || arr.length == 0 || aim < 0) {
			return -1;
		}
		int n = arr.length;
		int[][] dp = new int[n][aim + 1];
		//1、第一列
		for(int i = 0;i < n;i++) {
			dp[i][0] = 0;
		}
		//2、第一行
		int max = Integer.MAX_VALUE;//32位整数的最大值,表示 钱 换不开
		for(int j = 1;j <= aim;j++) {
			dp[0][j] = max;
			if(j - arr[0] >= 0 && dp[0][j - arr[0]] != max) {
				dp[0][j] = dp[0][j - arr[0]] + 1;
			}
		}
		//3、dp[i][j]
		int left = max;
		for(int i = 1;i < n;i++) {
			for(int j = 1;j <= aim;j++) {
				//至少使用一张arr[i]
				if(j - arr[i] >= 0 && dp[i][j-arr[i]] != max) {
					left = dp[i][j - arr[i]] + 1;
				}
				dp[i][j] = Math.min(left, dp[i-1][j]);
			}
		}
		
		return dp[n-1][aim] == max ? -1 : dp[n-1][aim];
	}

设 N = arr.length   dp[N][aim+1]  : dp[ i ][ j ]表示在可以任意使用arr[ 0...i ]货币(每个仅代表一张货币)的情况下,组成 j 所需的最小张数

  1. 第一行 : dp[0][0..j..aim] 表示只使用一张arr[0]货币的情况下,换钱数为 j 的最小张数;
  2. 第一列 : dp[0.. i .. N-1][0] 表示要换的钱数为 0 时需要的最小张数  dp[0..i..N][0] = 0;
  3. dp[ i ][ j ] 的值的可能取值:
  • 完全不使用arr[ i ]货币(任意使用arr[ 0... i-1 ]货币) : 即dp[ i - 1 ][ j ] 
  • 使用一张 arr[i] 货币,在任意使用arr[ 0... i-1 ]货币的情况下,组成 j - arr[i] 所需的最小张数: 即 dp[ i-1 ][ j - arr[i] ] + 1 

==> 如果 dp[ i-1 ][ j - arr[i] ] 中 j - arr[i] < 0,说明 arr[ i ] 太大,只用一张 arr[ i ] 都会超过钱数 j ,令dp[ i ][ j ] =  dp[ i - 1 ][ j ]

        否则,dp[ i ][ j ] = min{ dp[ i - 1 ][ j ], dp[ i-1 ][ j - arr[i] ] + 1 } 

 代码

public static int myCoins2(int[] arr,int aim) {
		if(arr == null || aim < 0 || arr.length == 0) return -1;
		int n = arr.length;
		int[][] dp = new int[n][aim + 1];
		//1、第一列
		for(int i = 0;i < n;i++) {
			dp[i][0] = 0;
		}
		//2、第一行
		int max = Integer.MAX_VALUE;
		for(int j = 1;j <= aim;j++) {
			dp[0][j] = max;
		}
		//arr[0] 仅有一张
		if(arr[0] <= aim) {
			dp[0][arr[0]] = 1;
		}
		//3、dp[i][j]
		int leftUp = 0;
		for(int i = 1;i < n;i++) {
			for(int j = 1;j <= aim;j++) {
				leftUp = max;
				//使用arr[i]
				if(j - arr[i] >= 0 && dp[i-1][j-arr[i]] != max) {
					leftUp = dp[i-1][j-arr[i]] + 1;
				}
				dp[i][j] = Math.min(dp[i-1][j], leftUp);
			}
		}
		
		return dp[n-1][aim] != max ? dp[n-1][aim] : -1;
	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值