关键点:
设 N = arr.length dp[N][aim+1] : dp[ i ][ j ]表示在可以任意使用arr[ 0...i ]货币的情况下,组成 j 所需的最小张数
- 第一行 : dp[0][0..j..aim] 表示只使用arr[0]货币的情况下,换钱数为 j 的最小张数;
- 第一列 : dp[0.. i .. N-1 ][0] 表示要换的钱数为 0 时需要的最小张数 dp[0..i..N][0] = 0;
- 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 所需的最小张数
- 第一行 : dp[0][0..j..aim] 表示只使用一张arr[0]货币的情况下,换钱数为 j 的最小张数;
- 第一列 : dp[0.. i .. N-1][0] 表示要换的钱数为 0 时需要的最小张数 dp[0..i..N][0] = 0;
- 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;
}