使用记忆搜索方法优化暴力递归(通解)

题目:
给定一个数组arr,arr中所有的值均为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim代表要找的钱数,求换钱有几种方法?
一、暴力搜索方法
一个案例:
arr={5,10,25,1} aim = 1000
解法思路:
1.先用0个5,让[10,25,1]组下剩下的1000,最终的方法数记为 res1
2.用一个5,让[10,25,1]组下剩下的995,最终的方法数记为 res2
3.用2个5,让[10,25,1]组下剩下的990,最终的方法数记为 res3

201.用200个5,让[10,25,1]组下剩下的0,最终的方法数记为res201
所有res相加即为最终的结果数
这显然是一个可以使用暴力递归解决的问题,定义递归函数: int p1(arr, index, aim)它的含义是如果使用arr(index…N-1)这些面值的钱组成aim,返回的总方法数,具体代码如下:

public int coins1(int[] arr, int aim) {
	if(arr == null || arr.length == 0 || aim < 0) {
		return 0;
	}
	return process1(arr, 0, aim);
	
}

private process1(int[]arr, int index, int aim) {
	int res = 0;
	if(index == arr.length) {
		res = aim == 0 ? 1 : 0;
	} else {
		for(int i =0; arr[index] * i < = aim; i++) {
			res += process1(arr, index + 1, aim - arr[index] * i);
		}
	}
}

以上代码为暴力递归方法,暴力之所以暴力就是因为这段代码中存在着很多重复的计算,比如,当我们想要使用0张5元和1张10元时剩下的aim = 990时的方法与我们此刻使用2张5元和0张10元时所对应aim时的方法其实应该是同一回事(也就是只去求p1(arr, 2, 990)的返 回值),但是暴力递归便会重复计算。这种其实在保留递归中非常常见,因此我们就需要进行优化暴力递归的算法。

二、记忆搜索的方法
使用记忆搜索的方法的时候,我们使用的题目的场景还是上面的场景。
但是去分析递归函数p1(arr, index, aim)的时候,我们发现其实arr是始终不变的,变化的只有index和aim的值,因此我们递归方法可以变为p(index, aim),大量重复计算之所以会发生其实就是很多重复递归的结果并没有记录下来,所以对于同样的实参传过来我们还得 重复去求,所以我们可以事先准备好一张map表,把index和aim组成的共同结果组成key,返回结果为value放到map中,在下次进行递归之前,我们现在这张hash表中查询这个递归过程是否已经计算过了,如果计算过了那么我直接从这个hash表里面直接取值就ok,如果这 个key不存在我再去进行递归求值就可以
代码如下:

public int coins2(int [] arr, int aim) {
	if(arr == null || arr.length == 0 || aim < 0) {
		return 0;
	}
	int [] [] map = new int[arr.length + 1][aim + 1];
	return process2(arr, 0, aim, map);
}

private int process2(int [] arr, int index, int aim, int [][] map) {
	int res = 0;
	if(index == arr.length) {
		res = aim == 0 ? 1 : 0;
	} else {
		int mapValue = 0;
		for(int i = 0; arr[index] * i < aim; i++) {
			mapValue = map[index + 1][aim - arr[index] * i];
			if(mapValue != 0){
				res += mapValue == -1 ? 0 : mapValue;
			} else {
				res += process2(arr, index + 1, aim - arr[index] * i, map);
			}
		}
	}
	map[index][aim] = res == 0 ? -1 : res;
	return res;

}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值