换钱的方法数

题目描述:
在这里插入图片描述
思路分析:
以arr[5,10,2b5,1],aim = 15为例子。我们想得到aim=15的方法数sum。我们假设使用0张5元,其他面值组成15元的方法数为a,使用1张5元,其c他面值组成10元的方法数为b,使用2张5元,其他面值组成5元的方法数为c,使用3张5元,其他面值组成0元的方法数为d。sum = a+b+c+d。因此我们可以写出暴力递归的方法。
暴力递归方法如下:

    // arr:钱的面值
	// index:index以及往后的面值都可以随便用
	// aim:目标钱
	// 返回值:从index开始,得到aim的方法数
	public static int ways(int[] arr, int index, int aim) {
		int N = arr.length;
		int res = 0;
		if (index == N) {
			return aim == 0 ? 1 : 0;
		}
		for (int zhang = 0; arr[index] * zhang <= aim; zhang++) {
			int nextAim = aim - arr[index] * zhang;			
				res += ways(arr, index + 1, nextAim);						
		}
		return res;
	}

优化1:
写完暴力递归之后我们发现,在递归的过程中出现了很多重复计算。当index和aim定的时候,返回值都是一样的,并且和之前如何来到这个状态无关,我们称这种现象为无后效性。既然出现了很多重复计算,那我们可以使用hashmap把每次计算的值进行保存,当再使用到这个结果时,可以直接获取不用计算。这就是记忆性递归。
记忆性递归代码如下:

public static HashMap<String, Integer> hashMap = new HashMap<>();
	public static int ways_map(int[] arr, int index, int aim) {
		int N = arr.length;
		int res = 0;
		if (index == N) {
			return aim == 0 ? 1 : 0;
		}
		for (int zhang = 0; arr[index] * zhang <= aim; zhang++) {
			int nextAim = aim - arr[index] * zhang;
			String key = String.valueOf(index+1) +"_"+String.valueOf(nextAim);
			if(hashMap.containsKey(key))
			{
				res+=hashMap.get(key);
			}else {
				res += ways(arr, index + 1, nextAim);
			}			
		}
		hashMap.put(String.valueOf(index)+"_"+String.valueOf(aim), res);
		return res;
	}

优化2:
记忆性递归已经可以提高很多的效率了,但是我们还可以使B用动态规C划再次优化。动态规划就是一个填dp表的过程,我们根据暴力递归的BaseCase,可以得到初始表的一些值,然后根据位置关系,把整张表填好,结果自然而然就出来了。
动态规划代码如下:

public static int ways_dp(int[] arr, int index, int aim) {
		int N = arr.length;
		if(index<0||index>N||aim<0)
			return 0;		
		int[][] dp = new int[N+1][aim+1];
		for(int i = 0;i<=N;i++)
		{
			dp[i][0] = 1;
		}
		for(int i = 1;i<=aim;i++)
		{
			dp[N][i] = 0;
		}
		for(int i = N-1;i>=0;i--)
		{
			for(int j = 1;j<=aim;j++)
			{
				dp[i][j] = dp[i+1][j];
				dp[i][j] +=j-arr[i]>=0?dp[i][j-arr[i]]:0;
			}
		}
		return dp[0][aim];
	}

总结:
所有的动态规划都是从暴力递归优化过来的,要想写出动态规划就要先写出暴力递归的代码,真正理解递归的过程,才能明白动态规划。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值