给一个int []整数数组,[2,7,3,5,3]会有重复值,现在给aim=10,组成10最少用多少个硬币?
public static int coinsWay1(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0;}return process1(arr, 0, aim);}// 暴力递归// arr[index...]组成rest这么多钱,最少的硬币数量返回。// arr硬币都在里面// index:自由选择arr[index...]// rest:剩余到达成的目标// 组成rest这么多钱,返回最少的硬币数public static int process1(int[] arr, int index, int rest) {if(rest < 0){return -1; // 代表到最后怎么都组成都组成不了}if(rest == 0){return 0; // 说明已经搞定了aim,需要0枚}// rest>0情况// index == arr.length 没有硬币了,没有这样的方案if(index == arr.length){return -1;}// rest>0,并且也有硬币// 不选择当前硬币,和选择当前硬币// 不选择index硬币int p1 = process1(arr, index + 1, rest);// 选择当前硬币int p2Next = process1(arr, index + 1, rest - arr[index]);if(p1 == -1 && p2Next == -1){return -1;}else{if(p1 == -1){ // 决策p1无效,返回p2 , +1是因为用了当前idnex硬币return p2Next + 1;}if(p2Next == -1){ // 决策p2无效,返回p1return p1;}return Math.min(p1, p2Next+1); // 都有效,决策选择出用的硬币最少的,无效解-1会干扰决策}// 不能这样,等于-1时会影响到决策// return Math.min(process1(arr, index + 1, rest),// 1 + process1(arr, index + 1, rest - arr[index]));}// 记忆化搜索,搞定可变参数组合public static int coinsWay2(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0;}int [][] dp =new int [arr.length+1][aim+1];for (int i = 0; i <= arr.length; i++) {for (int j = 0; j <= aim; j++) {dp[i][j] = -2; //表示没计算过}}return process2(arr, 0, aim, dp);}// arr硬币都在里面// index:自由选择arr[index...]这些硬币// rest:剩余到达成的目标// 组成rest这么多钱,返回最少的硬币数public static int process2(int[] arr, int index, int rest,int[][] dp) {// 认为中了无效缓存if(rest<0){return -1; // 直接放前面,也算一种缓存}if(dp[index][rest]!= -2){return dp[index][rest];}if(rest==0){dp[index][rest]=0;}else if(index == arr.length){// rest>0,没有硬币dp[index][rest]=-1;}else{int p1 = process2(arr, index + 1, rest,dp);int p2Next = process2(arr, index + 1, rest - arr[index],dp);if(p1 == -1 && p2Next == -1){dp[index][rest]=-1;}else{if(p1 == -1){ // 决策p1无效,返回p2dp[index][rest]= p2Next + 1;}else if(p2Next == -1){ // 决策p2无效,返回p1dp[index][rest]= p1;}else{dp[index][rest]= Math.min(p1, p2Next+1); // 都有效,决策}}}return dp[index][rest];}// 动态规划public static int coinsWay3(int[] arr, int aim) {if (arr == null || arr.length == 0 || aim < 0) {return 0;}int N=arr.length;int [][] dp =new int [N+1][aim+1];// base casefor (int row = 0; row <= N; row++) {dp[row][0]=0;}// base casefor (int col = 1; col <= aim; col++) {dp[N][col] = -1;}// n行填好, 从n-1行开始,从左往右,从下向上for (int index = N-1; index >=0; index--) {// 0列填好,从开始for (int rest = 1; rest <= aim; rest++) {// 把递归代码黏贴过来改// 递归过程变成了,动态规划格子取值过程int p1 = dp[index+1][rest];int p2Next = -1; // 越界-1 ,没越界赋值,注意边界if(rest - arr[index]>=0){ // [rest - arr[index]]可能越界所以要这样写p2Next = dp[index+1][rest - arr[index]];}if(p1 == -1 && p2Next == -1){dp[index][rest] = -1;}else{if(p1 == -1){ // 决策p1无效,返回p2dp[index+1][rest]= p2Next + 1;}if(p2Next == -1){ // 决策p2无效,返回p1dp[index+1][rest]= p1;}dp[index+1][rest]= Math.min(p1, p2Next+1); // 都有效,决策 ,因为最后一行是-1还是有干扰}}}// 最终返回return dp[0][aim];}
