Recursion 硬币组合问题 @CareerCup

本文探讨了一道经典的递归问题——使用不同面额的硬币组成特定金额的方法数。通过对比两种递归算法,强调了如何避免重复计算,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

非常有意义的一道题,关键的一点要知道DFS是有顺序的,即在DFS中(5,1)和(1,5)是不一样的组合。所以对这道题我的第一种解法就重复算了多次,具体如图所示:当计算f(10)时,DFS给出的结果是9次,但实际上应该是4.

去重的方法是多用一个变量(level)来限制条件。这样能保证在访问对于给定level只会深搜小于等于level的分支。


同样参考了http://hawstein.com/posts/8.7.html

但,这是错误的。问题出在哪?有序与无序的区别!这个函数计算出来的组合是有序的, 也就是它会认为1,5和5,1是不一样的,导致计算出的组合里有大量是重复的。 那要怎么避免这个问题?1,5和5,1虽然会被视为不一样,但如果它们是排好序的, 比如都按从大到小排序,那么就都是5,1了,这时就不会重复累计组合数量。 可是我们总不能求出答案来后再搞个排序吧,多费劲。这时我们就可以在递归上做个手脚, 让它在计算的过程中就按照从大到小的币值来组合。比如, 现在我拿了一个25分的硬币,那下一次可以取的币值就是25,10,5,1;如果我拿了一个 10分的,下一次可以取的币值就只有10,5,1了;这样一来,就能保证,同样的组合, 我只累计了一次




package Recursion;

/**
 * Given an infinite number of quarters (25 cents), dimes (10 cents), nickels (5
 * cents) and pennies (1 cent), write code to calculate the number of ways of
 * representing n cents.
 * 
 * 译文:
 * 
 * 我们有25分,10分,5分和1分的硬币无限个。写一个函数计算组成n分的方式有几种?
 * 
 */
public class S9_8 {

	public static void main(String[] args) {
		System.out.println(recWrong(10));
		System.out.println(recCorrect(10, 10));
	}

	public static int recWrong(int n) {
		if (n < 0) {
			return 0;
		}
		if (n == 0) {
			return 1;
		}

		int ways = 0;

		ways += recWrong(n - 25);
		ways += recWrong(n - 10);
		ways += recWrong(n - 5);
		ways += recWrong(n - 1);
		return ways;
	}

	// 增加一个level变量来控制重复的问题!
	public static int recCorrect(int n, int level) {
		if (n < 0) {
			return 0;
		}
		if (n == 0) {
			return 1;
		}

		int ways = 0;

		if (level >= 25) {
			ways += recCorrect(n - 25, 25);
		}
		if (level >= 10) {
			ways += recCorrect(n - 10, 10);
		}
		if (level >= 5) {
			ways += recCorrect(n - 5, 5);
		}
		if (level >= 1) {
			ways += recCorrect(n - 1, 1);
		}
		return ways;
	}

}


在Python中,分治算法可以用来解决“假硬币问题,也称为“找零钱”问题或“计数排序”变种。这个问题的基本设定是给你一堆真假未知的硬币,你需要找出其中有多少真硬币。给定一组硬币的面值和它们出现的次数,目标是确定至少一枚假硬币的存在,并尽可能地最小化其误差。 分治策略通常分为三个步骤: 1. **分割**(Divide):将硬币集分成两部分,通常是按照价值大小进行划分。 2. **递归**(Recursion):对每半部分分别应用相同的处理,直到每个子集只有一个硬币。 3. **合并**(Combine):比较分割后的结果,如果两部分的结果有差异,那么这个差异就是假硬币造成的。例如,如果一个部分比预期少的硬币较多,那说明这部分有更多的假币。 这里的关键在于每次递归都会尝试识别出哪些面额的硬币与期望的数量不符,最终通过累积错误找到假币。以下是简单的伪代码示例: ```python def find_fake_coins(coin_values, coin_counts, expected_sum): # 基线条件:只有一枚硬币,无需检查 if len(coin_values) == 1: return 0 if expected_sum == coin_counts[0] else 1 # 分治 half = len(coin_values) // 2 left_half_result = find_fake_coins(coin_values[:half], coin_counts[:half], expected_sum) right_half_result = find_fake_coins(coin_values[half:], coin_counts[half:], expected_sum - sum(coin_counts[:half])) # 合并结果 if abs(left_half_result - right_half_result) > 0: return max(left_half_result, right_half_result) # 返回更大的那一侧的假币数量 else: return min(left_half_result, right_half_result) # 如果两边一致,则选择较小的那个 # 示例用法 coin_values = [1, 2, 5] coin_counts = [3, 4, 9] expected_sum = 14 fake_coins = find_fake_coins(coin_values, coin_counts, expected_sum) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值