题目描述
题目来源
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
注意:
给定的火柴长度和在 0 到 10^9之间。
火柴数组的长度不超过15。
解题思路
- 对于给定的若干根火柴,我们需要将他们分为四组
- 每一组火柴的长度之和相同,等于所有火柴长度之和的四分之一
流程:
使用深度优先搜索,枚举出所有的分组情况,并对每一种情况,进行判断是否符合上面的情况。
我们对每一根火柴进行搜索,当搜索到第i
根火柴的时候,将其放到4组中的一组,对于放置的每一种方法,继续对i+1
根火柴进行递归搜索。当搜索完全部的火柴之后,判断每组的长度之和是否相同。
在进行搜索之前,我们可以将火柴的长度从大到小进行排序,方便我们先搜索较长的火柴。这样做的目的是对搜索进行剪枝,例如当火柴的长度为 [4,4,4,8]
时,,排序后数组为[8,4,4,4]
每条边的长度为 5,如果我们先搜索长度为 8 的火柴,就可以发现它无法被放在任意一组中,因此直接退出搜索返回 False。
在leetcode上面 不对数组进行排序可能会超时
代码实现
public class Solution {
public boolean makesquare(int[] nums) {
if (nums.length < 4) return false;
int sum = 0;
for (int i = 0; i < nums.length; i++){
sum+= nums[i];
}
if (sum%4 != 0) return false;
Integer[] newNums = new Integer[nums.length];
for (int i = 0; i < nums.length; i++){
newNums[i] = nums[i];
}
Arrays.sort(newNums, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
int[] bucket = new int[4];
return dfs(0, newNums, bucket, sum / 4);
}
private boolean dfs(int index, Integer[] nums, int[] bucket, int target){
if (index >= nums.length){
return bucket[0] == target && bucket[1] == target && bucket[2] == target && bucket[3] == target;
}
for (int i = 0; i<4; i++){
if (bucket[i] + nums[index] > target){
continue;
}
bucket[i] += nums[index];
if (dfs(index + 1, nums, bucket, target)){
return true;
}
bucket[i] -= nums[index];
}
return false;
}
public static void main(String[] args) {
System.out.println(new Solution().makesquare(new int[]{1,2,3,4,9,6,5,2}));
System.out.println(new Solution().makesquare(new int[]{1,1,2,2,2,1,1,1,1}));
}
}