一个int数组,里面无重复值,可以使用其中任意张,aim=10,用任意面值任意张,问方法数是多少?
public static int way1(int []arr,int aim){return process(arr,0,aim);}// 暴力递归// 可以自由使用arr[index...]所有的值 index// 需要搞定的钱数是rest// 返回找零的方法数public static int process(int []arr,int index,int rest){if(index==arr.length){ // 没有面值可以选了return rest == 0 ? 1: 0; // 要搞定剩下的钱数为零,则是一种方法}// arr[index] 0张、1张...去试,但不要超过rest的数int ways=0;for (int zhang = 0; arr[index]*zhang <= rest; zhang++) {// 根据当前货币挑出某些位置累加// rest - 已经搞的钱ways += process(arr, index + 1, rest - arr[index]*zhang);}return ways;}// DP// 有枚举行为 时间复杂度为O(aim*index)*O(aim) O(index*aim^2)public static int way2(int []arr,int aim){if(arr==null || arr.length ==0){return 0;}int N =arr.length;int [][] dp= new int [N+1][aim+1];dp[N][0] =1;for (int index = N-1; index >=0; index--) {for (int rest = 0; rest <= aim;rest++) {int ways=0;// 枚举没必要,等于本行向左偏移一位的ways值+下面的值a。for (int zhang = 0; arr[index]*zhang<rest; zhang++) {// 根据当前货币挑出某些位置累加,把递归行为改成dp中取值ways+=dp[index+1][rest-arr[index]*zhang];}dp[index][rest]=ways;}}return dp[0][aim];}// 优化完的DP// 斜率优化:填表的时候如果有枚举行为,看临近的位置能不能替换枚举行为。只和观察有关,和实际意义无关。统一技巧。public static int way3(int []arr,int aim){if(arr==null || arr.length ==0){return 0;}int N =arr.length;int [][] dp= new int [N+1][aim+1];dp[N][0] =1;// 自己下方格子的值+加左边格子的值// 因为自己左边格子值是枚举来的,不用枚举了for (int index = N-1; index >=0; index--) {for (int rest = 0; rest <= aim;rest++) {dp[index][rest]=dp[index+1][rest]; // 自己下方格子if(rest - arr[index] >=0){ // 别越界// rest减去自己用一张的值偏移dp[index][rest]+= dp[index][rest-arr[index]]; // 自己本行偏移一位的ways值}}}return dp[0][aim];}
