算法笔记练习 4.3 递归 问题 C: 神奇的口袋

算法笔记练习 题解合集

题目链接

题目

题目描述
有一个神奇的口袋,总的容积是40,用这个口袋可以变出一些物品,这些物品的总体积必须是40。John现在有n个想要得到的物品,每个物品的体积分别是a1,a2……an。John可以从这些物品中选择一些,如果选出的物体的总体积是40,那么利用这个神奇的口袋,John就可以得到这些物品。现在的问题是,John有多少种不同的选择物品的方式。

输入
输入的第一行是正整数n (1 <= n <= 20),表示不同的物品的数目。接下来的n行,每行有一个1到40之间的正整数,分别给出a1,a2……an的值。

输出
输出不同的选择物品的方式的数目。

样例输入

2
12
28
3
21
10
5

样例输出

1
0

思路

递归法

假定有 n 件物品,他们的重量存放于数组weight中,目标重量为target

要解决的问题:n 个物品正好凑成target,有几种方案?

拆分成子问题:

  1. 用重量为weight[0]的第 1 个物品,剩下的物品正好凑成target-weight[0],有几种方案?
  2. 不用第 1 个物品,剩下的物品正好凑成target,有几种方案?

首先,递归调用应该是这样的:在没遇到递归边界(即target > 0,还需要更多物品)的时候,方案数等于上述两个子问题的方案数相加。

然后来确定递归边界

  1. target == 0的时候,说明出现了一种可行的方案;
  2. 若 1 不成立,那么当target < 0或者n == 0(没有物品可用)的时候,不可能再出现可行方案。

注意 1 和 2 的优先级,如果先判断 2 再判断 1 的话,正好用上最后一个物品凑成target的情况会被误判。

代码

递归法

#include 
#define MAXN 20
#define VOLUME 40
/* countPlans
参数:	目标重量 target
		表示 n 个物品重量的数组 weights
返回用数组中的物品恰好配成目标重量的方案数量 */ 
int countPlans(int target, int n, int *weights);
int main(){
	int n, i;
	while (scanf("%d", &n) != EOF){
		int weights[n]; 
		for (i = 0; i < n; ++i)
			scanf("%d", &weights[i]);
		printf("%d\n", countPlans(VOLUME, n, weights));
	} 
	return 0;
} 

int countPlans(int target, int n, int *weights){
	if (target == 0) return 1;
	else if (n == 0 || target < 0) return 0;
	else return countPlans(target-weights[0], n-1, weights+1) 
		+ countPlans(target, n-1, weights+1);
} 

第一次 AC 代码

第一次做这道题,递归写得很罗嗦。
原因是边界条件没考虑好,写的过分“安全”了,然后为了匹配边界条件,把边界调用分成三种情况来讨论。
也记录一下代码,思路见注释。

#include 
#define MAXN 20
#define VOLUME 40
/* countPlans
参数:	目标重量 target
		表示 n 个物品重量的数组 weights
返回用数组中的物品恰好配成目标重量的方案数量 */ 
int countPlans(int target, int n, int *weights);
int main(){
	int n, i;
	while (scanf("%d", &n) != EOF){
		int weights[n]; 
		for (i = 0; i < n; ++i)
			scanf("%d", &weights[i]);
		printf("%d\n", countPlans(VOLUME, n, weights));
	} 
	return 0;
} 

int countPlans(int target, int n, int *weights){
	// 递归边界:还剩 0 个物品或者还需要 0 单位体积
	// 此时不可能会再有新方案了,返回 0 
	if (n == 0 || target == 0){
		return 0; 
	// 若第一个物品的重量等于剩余目标,有两种选择
	// 1. 直接将其纳入,补足剩余重量,这种选择的方案数量为 1 
	// 2. 不用该物品,这种选择的方案数量需要递归调用来得到
	} else if (weights[0] == target){
		return 1 + countPlans(target, n-1, weights+1);
	// 若第一个物品的重量小于剩余目标
	// 同样有两种选择,用或者不用第一个物品,都需要递归调用 
	} else if (weights[0] < target){
		return countPlans(target, n-1, weights+1)
			+ countPlans(target-weights[0], n-1, weights+1);
	// 若第一个物品的重量大于剩余目标,只能跳过
	} else return countPlans(target, n-1, weights+1);
} 

你可能感兴趣的:(算法笔记)