这个答案在精神上与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}));
}
}