还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2] 输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。 示例 2:
输入: [3,3,3,3,4] 输出: false
解释: 不能用所有火柴拼成一个正方形。 注意:
给定的火柴长度和在 0 到 10^9之间。 火柴数组的长度不超过15。
```java
public boolean makesquare(int[] nums) {
if (nums.length < 4) {
return false;
}
int sum = 0;
for (int i = nums.length - 1; i >= 0; i--) {
sum += nums[i];
}
//快速判断总和是否是4的倍数
if (sum % 4 != 0) {
return false;
}
Arrays.sort(nums);
int target = sum / 4;
//快速判断最大的数是否超过了四分之一
if (nums[nums.length - 1] > target) {
return false;
}
boolean[] visit = new boolean[nums.length];
//循环4次,从大到小每次找到一条符合条件的边
for (int i = 3; i >= 0; i--) {
if (!ok(target, nums, visit, nums.length - 1)) {
return false;
}
}
return true;
}
static boolean ok(int target, int[] nums, boolean[] visit, int startPos) {
//从大到小遍历,通过startPos计算当前遍历到的偏移量
for (int i = startPos; i >= 0; i--) {
//已经使用过的用visit标记,跳过
//nums[i]有可能大于当前剩余目标值,也要跳过
if (visit[i] || nums[i] > target) {
continue;
} else if (nums[i] == target) {
//找到目标值可以直接标记返回
visit[i] = true;
return true;
} else {
//找到当前小于目标值的最大值,标记后再往前寻找剩余目标值
visit[i] = true;
if (ok(target - nums[i], nums, visit, i - 1)) {
return true;
}
//到这里说明这次递归没有找到目标值,那么要将这次的标记还原
visit[i] = false;
}
}
//遍历完都没找到目标值,说明没有符合条件的情况
return false;
}