| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @param |
| | | * @author Ryan |
| | | * @description 根据对象多个属性合并处理 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/25 10:55 |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * @param |
| | | * @author Ryan |
| | | * @description 用于存储多个分组键的内部类 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/25 10:56 |
| | | */ |
| | |
| | | } |
| | | |
| | | |
| | | public static List<Double> findBestCombination(Double[] candidates, double targetSum) { |
| | | bestSolution = null; |
| | | target = targetSum; |
| | | minDifference = Double.MAX_VALUE; |
| | | Arrays.sort(candidates); |
| | | backtrack(candidates, 0, new ArrayList<>(), 0.0); |
| | | |
| | | // 如果没有精确解,返回最接近的近似解 |
| | | return bestSolution != null ? bestSolution : new ArrayList<>(); |
| | | } |
| | | |
| | | private static void backtrack(Double[] candidates, int start, |
| | | List<Double> current, double currentSum) { |
| | | // 计算当前和与目标的差值 |
| | | double difference = Math.abs(currentSum - target); |
| | | |
| | | // 如果找到更优解(差值更小或差值相同但组合更短) |
| | | if (difference < minDifference - EPSILON || |
| | | (Math.abs(difference - minDifference) < EPSILON && |
| | | (bestSolution == null || current.size() < bestSolution.size()))) { |
| | | minDifference = difference; |
| | | bestSolution = new ArrayList<>(current); |
| | | /** |
| | | * @param |
| | | * @return |
| | | * @author Ryan |
| | | * @description 获取全拖或非全拖库位 |
| | | * @time 2025/4/28 09:41 |
| | | */ |
| | | public static List<Integer> findCombination(double[] nums, Double target) { |
| | | // 处理等值情况 |
| | | List<Integer> equal = findEqual(nums, target); |
| | | if (equal != null && !equal.isEmpty()) { |
| | | return equal; |
| | | } |
| | | |
| | | // 如果已经找到精确解,不需要继续搜索更长的组合 |
| | | if (minDifference < EPSILON) { |
| | | // 处理大于的情况 |
| | | List<Integer> greater = findGreater(nums, target); |
| | | if (greater != null && !greater.isEmpty()) { |
| | | return greater; |
| | | } |
| | | |
| | | // 处理小于的情况 |
| | | List<Integer> less = findLess(nums, target); |
| | | return less != null ? less : new ArrayList<>(); // 确保不返回null |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 使用动态规划寻找和等于目标值的最少元素组合。动态规划数组dp记录达到每个和所需的最少元素数目,prev数组记录路径以便回溯找到具体索引。 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:33 |
| | | */ |
| | | private static List<Integer> findEqual(double[] nums, double target) { |
| | | int n = nums.length; |
| | | double[] dp = new double[(int) (target * 100 + 1)]; // 放大100倍处理精度问题 |
| | | Arrays.fill(dp, Double.MAX_VALUE); |
| | | dp[0] = 0; |
| | | int[] prev = new int[(int) (target * 100 + 1)]; |
| | | Arrays.fill(prev, -1); |
| | | |
| | | for (int j = 0; j < n; j++) { |
| | | int num = (int) (nums[j] * 100); // 放大100倍处理精度问题 |
| | | for (int i = (int) (target * 100); i >= num; i--) { |
| | | if (dp[i - num] != Double.MAX_VALUE && dp[i - num] + 1 < dp[i]) { |
| | | dp[i] = dp[i - num] + 1; |
| | | prev[i] = j; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (dp[(int) (target * 100)] == Double.MAX_VALUE) { |
| | | return null; |
| | | } |
| | | |
| | | List<Integer> indices = new ArrayList<>(); |
| | | int current = (int) (target * 100); |
| | | while (current > 0) { |
| | | int j = prev[current]; |
| | | if (j == -1) return null; |
| | | indices.add(j); |
| | | current -= (int) (nums[j] * 100); |
| | | } |
| | | |
| | | return indices; |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 将数组降序排序后,贪心地累加元素直到总和超过目标值,返回这些元素的索引 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:34 |
| | | */ |
| | | private static List<Integer> findGreater(double[] nums, double target) { |
| | | List<double[]> sorted = new ArrayList<>(); |
| | | for (int i = 0; i < nums.length; i++) { |
| | | sorted.add(new double[]{nums[i], i}); |
| | | } |
| | | sorted.sort((a, b) -> Double.compare(b[0], a[0])); |
| | | |
| | | double sum = 0; |
| | | List<Integer> indices = new ArrayList<>(); |
| | | for (double[] pair : sorted) { |
| | | sum += pair[0]; |
| | | indices.add((int) pair[1]); |
| | | if (sum > target) { |
| | | return indices; |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 使用回溯法生成所有可能的组合,寻找总和最大但不超过目标值的组合,并记录元素数目最少的组合。 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:34 |
| | | */ |
| | | private static List<Integer> findLess(double[] nums, double target) { |
| | | int n = nums.length; |
| | | List<Integer> bestIndices = new ArrayList<>(); |
| | | double[] maxSum = {-1.0}; |
| | | for (int k = 1; k <= n; k++) { |
| | | List<Integer> currentIndices = new ArrayList<>(); |
| | | double[] currentMax = {-1.0}; |
| | | backtrack(nums, target, 0, k, 0.0, 0, currentIndices, currentMax, new ArrayList<>()); |
| | | if (currentMax[0] != -1.0) { |
| | | if (currentMax[0] > maxSum[0] || (currentMax[0] == maxSum[0] && currentIndices.size() < bestIndices.size())) { |
| | | maxSum[0] = currentMax[0]; |
| | | bestIndices = new ArrayList<>(currentIndices); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return bestIndices.isEmpty() ? null : bestIndices; |
| | | } |
| | | |
| | | private static void backtrack(double[] nums, double target, int start, int k, double currentSum, int count, List<Integer> currentIndices, double[] currentMax, List<Integer> path) { |
| | | if (count == k) { |
| | | if (currentSum <= target && currentSum > currentMax[0]) { |
| | | currentMax[0] = currentSum; |
| | | currentIndices.clear(); |
| | | currentIndices.addAll(path); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 遍历候选数字 |
| | | for (int i = start; i < candidates.length; i++) { |
| | | double num = candidates[i]; |
| | | |
| | | // 剪枝:如果当前和已经远大于目标值,跳过 |
| | | if (currentSum + num > target + minDifference + EPSILON) { |
| | | continue; |
| | | } |
| | | |
| | | // 跳过重复数字 |
| | | if (i > start && Math.abs(candidates[i] - candidates[i - 1]) < EPSILON) { |
| | | continue; |
| | | } |
| | | |
| | | current.add(num); |
| | | backtrack(candidates, i + 1, current, currentSum + num); |
| | | current.remove(current.size() - 1); |
| | | for (int i = start; i < nums.length; i++) { |
| | | if (currentSum + nums[i] > target) continue; |
| | | path.add(i); |
| | | backtrack(nums, target, i + 1, k, currentSum + nums[i], count + 1, currentIndices, currentMax, path); |
| | | path.remove(path.size() - 1); |
| | | } |
| | | } |
| | | |