题目描述
某公司目前推出了AI开发者套件,AI加速卡,AI加速模块,AI服务器,智能边缘多种硬件产品,每种产品包含若干个型号。 现某合作商要采购金额为元的硬件产品搭建自己的AI基座。 例如当前库存有N种产品,每种产品的库存量充足,给定每种产品的价格,记为price(不存在价格相同的产品型号) 请为合作厂商列出所有可能的产品组合。
输入描述
输入包含采购金额和产品价格列表 第一行为 第二行为 例如
500
[100, 200, 300, 500]
输出描述
输出为组合列表。例如:
用例
输入
500
[100, 200, 300, 500, 500]
输出
[[100, 100, 100, 100, 100],[100, 100, 100, 200],[100, 100, 300],[100, 200, 200],[200, 300],[500],[500]]
[[100, 100, 100, 100, 100],[100, 100, 100, 200],[100, 100, 300],[100, 200, 200],[200, 300],[500],[500]]
题目解析
题目要求给出所有可能的方案,即总价格只要不超出采购金额即可,库存产品充足且能够重复选择。可重复全排列组合问题。
解决思路和步骤
- 首先很显然地,我们需要一个集合来存储结果。每一种符合结果的方案都将被保存。
- 首先每一次可以选择一种产品,从组合中选择任意一种产品,只要其不超过采购金额
- 如果选择任何一种产品都超过了采购金额,那么当前选择是一种方案,加入到结果集中去
- 即每一次进行剪枝,然后符合预期条件的加入到结果集合中去
- 那么对于每一种产品都扩展剪枝的话,难免会有重复,该如何高效避免重复呢?
- 经过观察发现,可以对产品的价格进行排序
- 对于每一种产品,仅仅需要对大于等于其价格的产品进行剪枝即可
代码实现思路
- 首先存储结果集需要一个
list
集合 - 每一次选择一个结果,需要把结果存储到一个
list
集合中去
- 是否能保存当前路径和以达到减少计算的目的呢?可以试试,那么初始化和为0
public class SalesBoost {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int amount = Integer.parseInt(in.nextLine());
String line = in.nextLine();
String replace = line.replace("[", "").replace("]", "").replace(" ", "");
String[] split = replace.split(",");
int[] price = new int[split.length];
for (int i = 0; i < split.length; i++) {
price[i] = Integer.parseInt(split[i]);
}
salesBoost(amount, price);
}
private static void salesBoost(int amount, int[] price) {
// 用来存储结果集
List<List<Integer>> ans = new ArrayList<>();
// 用来保存dfs过程中的实际结果
LinkedList<Integer> list = new LinkedList<>();
// 那么现在该考虑签名函数的入参
// list:需要用来存储临时结果集合
// ans:用来存储符合条件的结果集合
// price:从price里面选取产品
// 价格排序.
Arrays.sort(price);
dfs(amount, price, list, ans, 0, 0);
System.out.print("[");
for (int i = 0; i < ans.size(); i++) {
List<Integer> an = ans.get(i);
System.out.print("[");
for (int j = 0; j < an.size(); j++) {
System.out.print(an.get(j));
if(j != an.size() - 1) {
System.out.print(", ");
}
}
System.out.print("]");
if(i != ans.size() - 1) {
System.out.print(",");
}
}
System.out.print("]");
}
private static void dfs(int amount, int[] price, LinkedList<Integer> list, List<List<Integer>> ans, int idx, int sum) {
// 那么这里如何判断终止条件?
// 终止条件是:当前元素加上其所能选择的最小元素之后超过了amount
if(sum == amount) {
// 这里表示了,该种情况已经符合方案了
ans.add(new ArrayList<>(list));
return;
}
if(sum + price[idx] > amount || sum > amount) {
return;
}
for(int i = 0;i < price.length;i++) {
// 选择一个元素,这个元素的索引不能小于当前元素的索引,应该只处理大于等于 idx 元素
if(i >= idx) {
list.add(price[i]);
sum += price[i];
dfs(amount, price, list, ans, i, sum);
list.removeLast();
sum -= price[i];
}
}
}
}