c++ 递归问题之——合理消费

题目描述


已知B的饭卡里有钱p元,给定食堂物品的价格列表(价格不重复),请你给出所有可能的恰好把p元花掉的购买选择。
如:p=7, 食堂现有价格为 [3,2,6,7]  的物品,则可能的购买选择有:
[3, 2, 2]
[7]
每种价格的物品可以多次选购。

关于输入

n+1行
第1行有两个整数,分别为物品种类n, 以及B卡里多少钱p。
接下来有n行,每行一个数,为每种物品的价格xi。
满足:3<= n <= 10;2<= p <= 200;2<= xi <=100。

关于输出

所有可能的购买选择,每种选择一行,要求不重复。
对于每一种选择,价格的顺序要求按照输入顺序。
每种物品可以多次选购无限制。
如果没有恰好花掉的选择,则输出"NO"。

例子输入
4 7
3
2
6
7
例子输出
3 2 2
7

分析

这是一个很正常的递归问题。由于每种商品可以多次选购,所以,不妨设选购商品价格为a1,a2,……,an;且价格顺序按照输入顺序,则在第i次购买时,若购买的是aj,则在第i+1次购买时,购买的物品必然是aj或aj之后的物品,由此可找出前后两次购买之间的联系。此外,笔者认为,本题比较重要的一个点在于,在判断完某种购买方案是否可行后,要如何输出这种购买方案。由于物品可以购买多次,所以很自然地想到新定义一个数组b[10]来记录ai物品被购买的次数,从而在输出时,可以通过读取b[10]中的数据来决定某一个物品要输出的次数。

程序不难写出:

#include<iostream>
using namespace std;
int p, n;
int i;
int a[10];//用于表示各种物品的价格
int b[10]{};//用于记录每一种物品被买的次数
int flagg = 0;//这就是一个很普通的flag,用于判断是否存在能够正好把钱花完的购买方法,因为flag在whether函数中用了,又懒得起名字,所以就再加了一个g
int whether(int p, int i) {//p表示还剩下的金额,i表示接下来的凑单金额由a[i]开始分析
	int j;
	int k;
	int flag = 0;//定义一个flag,便于随后满足输出空格的格式
	if (p == 0) {//如果p==0,表示剩下金额为0,即饭卡里的钱被成功花完,此时可以返回买的物品价格
		flagg = 1;
		for (j = 0; j < n; j++) {
			for (k = 0; k < b[j]; k++) {
				if (flag == 0) {
					cout << a[j];
				}
				else {
					cout << " " << a[j];
				}
				flag = 1;
			}
		}//输出购买方法
		cout << endl;
	}
	if (p > 0) {//p > 0时,金额还未花完
		for (j = i; j < n; j++) {
			b[j]++;//不妨设这一次买的是a[j],则a[j]的购买次数b[j]+1
			whether(p - a[j], j);//此时, 饭卡内金额减少a[j],同时,下一次买的物品必然从a[j]开始
			b[j]--;//由于要求出所有方法,因此在分析完购买a[j]的情况后,应该恢复原有状态,然后分析购买a[j+1]的情况
		}
	}
	return 0;
}//注意到,这个函数中并没有讨论当p<0时的情况,这是因为当p<0时,函数自动跳过上面的几个if程序,而直接执行return 0,不影响函数的输出
int main() {
	cin >> n >> p;
	for (i - 0; i < n; i++) {
		cin >> a[i];
	}
	whether(p, 0);
	if (flagg == 0) {//如果flagg==0,说明不存在一种满足条件的购买方法,于是输出“NO”
		cout << "NO" << endl;
	}
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值