题目描述
已知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;
}

156

被折叠的 条评论
为什么被折叠?



