题目描述

某公司目前推出了AI开发者套件,AI加速卡,AI加速模块,AI服务器,智能边缘多种硬件产品,每种产品包含若干个型号。 现某合作商要采购金额为硬件产品销售方案_System元的硬件产品搭建自己的AI基座。 例如当前库存有N种产品,每种产品的库存量充足,给定每种产品的价格,记为price(不存在价格相同的产品型号) 请为合作厂商列出所有可能的产品组合。

输入描述

输入包含采购金额硬件产品销售方案_System和产品价格列表硬件产品销售方案_结果集_03 第一行为硬件产品销售方案_System 第二行为硬件产品销售方案_结果集_03 例如

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]]

题目解析

题目要求给出所有可能的方案,即总价格只要不超出采购金额即可,库存产品充足且能够重复选择。可重复全排列组合问题。


解决思路和步骤

  1. 首先很显然地,我们需要一个集合来存储结果。每一种符合结果的方案都将被保存。
  2. 首先每一次可以选择一种产品,从组合中选择任意一种产品,只要其不超过采购金额
  1. 如果选择任何一种产品都超过了采购金额,那么当前选择是一种方案,加入到结果集中去
  2. 即每一次进行剪枝,然后符合预期条件的加入到结果集合中去
  3. 那么对于每一种产品都扩展剪枝的话,难免会有重复,该如何高效避免重复呢?
  1. 经过观察发现,可以对产品的价格进行排序
  2. 对于每一种产品,仅仅需要对大于等于其价格的产品进行剪枝即可

代码实现思路

  1. 首先存储结果集需要一个list集合
  2. 每一次选择一个结果,需要把结果存储到一个list集合中去
  1. 是否能保存当前路径和以达到减少计算的目的呢?可以试试,那么初始化和为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];
            }
        }
    }


}