首先,我们需要明确几个关键点。题目给出的是一个整数数组
matchsticks
,数组中的每个元素表示火柴棒的长度。
我们的目标是判断是否能用这些火柴棒拼成一个正方形。拼成正方形的要求很简单,四条边的长度要相等。不能折断火柴棒,必须用所有火柴棒拼成正方形,并且每根火柴棒只能用一次。
为了解决这个问题,首先我们需要计算出正方形的周长。正方形的四条边总长度应该是所有火柴棒长度之和。我们可以先求出所有火柴棒的总长度,然后判断这个总长度是否能被4整除。
如果不能整除4,那就不可能拼成正方形,直接返回
false
。如果能够整除,那么每条边的长度应该是总长度的四分之一。
接下来,我们可以把问题转化为一个子集求和问题。我们要找到四个子集,每个子集的和都等于总长度的四分之一。为了高效地找到解,我们可以使用回溯算法来试着分配火柴棒,看看是否能成功找到四个和相等的子集。
具体的实现步骤如下:
-
-
判断总长度是否能被4整除,如果不能,直接返回
false
。
-
-
然后我们就要用回溯法来尝试把火柴棒分配到四条边上。
-
如果分配成功,返回
true
;如果回溯到某一步发现无法继续分配,就返回
false
。
代码如下:
import java.util.Arrays;
public class Solution {
public boolean makesquare(int[] matchsticks) {
// 计算火柴棒总长度
int totalLength = 0;
for (int matchstick : matchsticks) {
totalLength += matchstick;
}
// 如果总长度不能被4整除,直接返回 false
if (totalLength % 4 != 0) {
return false;
}
// 每条边的长度
int sideLength = totalLength / 4;
// 对火柴棒长度进行降序排序,优化回溯的效率
Arrays.sort(matchsticks);
reverse(matchsticks);
// 用来记录每条边当前的长度
int[] sides = new int[4];
// 回溯法尝试分配火柴棒
return backtrack(matchsticks, 0, sides, sideLength);
}
private boolean backtrack(int[] matchsticks, int index, int[] sides, int target) {
// 如果所有火柴棒都已经分配完,检查四条边是否都达到了目标长度
if (index == matchsticks.length) {
return sides[0] == target && sides[1] == target && sides[2] == target && sides[3] == target;
}
// 当前火柴棒的长度
int currentStick = matchsticks[index];
// 尝试将当前火柴棒放到每一条边上
for (int i = 0; i 4; i++) {
// 如果当前边的长度加上当前火柴棒长度超过目标,跳过
if (sides[i] + currentStick > target) {
continue;
}
// 如果当前边长度为0,表示这一条边还没有放任何火柴棒,那么跳过这一条边的重复尝试
if (i > 0 && sides[i] == sides[i - 1]) {
continue;
}
// 放入当前火柴棒
sides[i] += currentStick;
if (backtrack(matchsticks, index + 1, sides, target)) {
return true;
}
// 如果放入当前火柴棒后无法完成任务,撤回
sides[i] -= currentStick;
}
return false;
}
// 反转数组(降序排序)
private void reverse(int[] matchsticks) {
int left = 0, right = matchsticks.length - 1;
while (left int temp = matchsticks[left];
matchsticks[left] = matchsticks[right];
matchsticks[right] = temp;
left++;
right--;
}
}
}
这段代码中,我们首先计算了所有火柴棒的总长度,然后判断是否可以均匀分配到四条边。如果总长度可以被4整除,就将问题转化为回溯问题,尝试将火柴棒分配到四条边上。为了提高效率,我们在分配时对火柴棒进行了降序排序,这样可以减少不必要的回溯。
接下来是回溯算法的核心部分。我们从第一个火柴棒开始,尝试将它放到四条边上。每次放入火柴棒后,我们递归地尝试将剩余的火柴棒继续分配。如果某次递归无法完成,我们就撤回当前的选择,继续尝试其他的可能性。