这个答案在精神上与Joop Eggen 的答案相似。它实现了检查selectedTotal 和 discardedTotal 的(小)优化以中止分支,如果它超过目标(请注意,这假定所有值都是正数;如果不是这种情况,最小值是 x < 0,您只需将 -x 添加到所有值,运行算法,然后从答案中减去 -x。
输出与原始帖子中指定的完全相同 - 由于它仅在找到完整解决方案时生成,因此此代码应该比不断添加和删除值的算法更快部分答案,例如 Joop 的(selectedNumbers.add 后跟 selectedNumbers.remove,结果证明它不起作用;设置操作可能很快,但不执行它们甚至更快!)。
public class Main {
public static boolean search(int[] values, int goal, int index,
int selectedTotal, int discardedTotal,
List<Integer> selected, List<Integer> discarded) {
if (selected.size() == values.length/2 &&
discarded.size() == values.length/2) {
return selectedTotal == goal;
}
if (selectedTotal > goal ||
discardedTotal > goal ||
index == values.length) {
return selectedTotal == goal;
}
// try selecting value at index ...
if (selected.size() < values.length/2 &&
search(values, goal, index + 1,
selectedTotal + values[index], discardedTotal,
selected, discarded)) {
selected.add(values[index]);
return true;
}
// ... and, if that did not work, try discarding value at index
if (discarded.size() < values.length/2 &&
search(values, goal, index + 1,
selectedTotal, discardedTotal + values[index],
selected, discarded)) {
discarded.add(values[index]);
return true;
}
return false;
}
public static List<Integer> solve(int[] values) {
Arrays.sort(values);
int goal = IntStream.of(values).sum() / 2;
List<Integer> selected = new ArrayList<>();
List<Integer> discarded = new ArrayList<>();
if ( ! search(values, goal, 0,
0, 0, selected, discarded)) {
throw new IllegalArgumentException("This puzzle cannot be solved");
}
Collections.reverse(selected);
Collections.reverse(discarded);
selected.addAll(discarded);
return selected;
}
public static void main(String[] args) {
System.out.println(solve(new int[] {16,22,35,8,20,1,21,11}));
}
}