我正在尝试这个LeetCode question,它要求一个布尔函数,如果4个int的给定数组可以通过基本算术运算符+
,-
得到24,则返回true, /
,*
。括号不重要,因为如果所有排列都正确,它们将被隐含地计算。
对于这个问题,我使用(n P k)表示从n个元素中找到所有k大小的排列,因为顺序对-
和/
很重要。
对于第一个选择,我们有(4 P 2)= 12和4个运算符,因此12 * 4 = 48个结果。对于第二种选择,我们剩下3个元素,或者(3 P 2)= 6和4个运算符,6 * 4 = 24个结果。最后,我们有2个元素和4个运算符,总共8个结果。总的来说,我们有48 * 24 * 8 = 9216 元素和运算符的总排列。
我的解决方案适用于69/70的LeetCode测试用例(列表3,3,8,8错误地返回false)。但是,无论何时我尝试调整代码以使最后一个测试用例工作,一组新的测试用例都会失败。代码似乎是正确的,但显然它不是,经过几个小时和几个小时后,我仍然难以接受。
首先,一个帮助方法交换数组中的两个元素。
// swap indices x and y of array A
static void swap(double[] A, int x, int y) {
double temp = A[x];
A[x] = A[y];
A[y] = temp;
}
核心思想是将剩余的值保留在A[0..right]
中(注意包含上限)。指示i
和j
表示为操作选择的2个元素。将此新结果放在i
。
现在A[j]
用于该计算,需要被删除"。这是通过将j
与right
进行交换来完成的。在递归调用中,递减right
,因为您将2个值与某个运算符组合在一起,现在剩下少1个元素。所以现在j
位于数组的右边分区中,不再用于将来的计算。
/**
* Iterate through all 9216 permutations of elements and operators until a permutation
* that can make 24 has been found.
*
* @param A Array of doubles
* @param right Rightmost index of current subarray A[0..right],
which stores the remaining elements
* @return True if the original given array can produce 24
*/
static boolean permute(double[] A, int right) {
// base case, array of size 1
if (right == 0) { return A[0] == 24; }
// choose 2 distinct elements and apply all 4 operators
for (int i = 0; i <= right; i++) {
double temp = A[i]; // used for restoring value later
for (int j = 0; j <= right; j++) {
if (i==j) continue;
for (int op = 0; op < 4; op++) {
switch(op) {
case 0: A[i] += A[j]; break;
case 1: A[i] *= A[j]; break;
case 2: { if(A[j] <= 0) return false;
A[i] /= A[j]; break; }
case 3: A[i] -= A[j]; break;
}
swap(A,j,right);
if(permute(A,right-1)) return true;
// restore values
swap(A,j,right);
A[i] = temp;
}
}
}
return false;
}
在调试时,我添加了print语句,对于每一种情况,无论实际的布尔值是对还是错,都打印了9216个数组,所以我知道迭代次数是正确的。我怀疑在内部for
循环中的递归调用后恢复值时发生错误。
允许与int[]
static boolean make24(int[] A) {
return permute(IntStream.of(A).mapToDouble(num -> num).toArray(), A.length-1);
}
最后,驱动方法:
public static void main(String[] args) {
int[] a = new int[] {8,8,3,3};
System.out.println(make24(a)); // incorrectly returns false
}
可能有用的其他测试用例:
1,3,4,6 --> true
3,3,8,8 --> true
1,2,1,2 --> false
3,4,6,7 --> false
1,5,9,1 --> false
我尽可能地尝试评论和解释而不会过度。我在这里要求很多,所以我不期待很多回复,但是感谢你阅读这篇文章!
编辑:从此solution I found开始,任意精度算术似乎都会导致一些错误。例如,代替return A[0] == 24;
,执行return Math.abs(A[0]-24) < 1e-8;
。进行这些更改会修复程序,因此实际的算法逻辑是正确的:)