题目
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
注意:
- 给定的火柴长度和在 0 到 10^9之间。
- 火柴数组的长度不超过15。
分析
因此,对于给定的若干根火柴,我们需要:
- 将它们分成四组,每一根火柴恰好属于其中的一组;
- 每一组火柴的长度之和都相同,等于所有火柴长度之和的四分之一。
dfs 深度优先搜索
我们可以使用深度优先搜索枚举出所有的分组情况,并对于每一种情况,判断是否满足上述的两个条件。
我们依次对每一根火柴进行搜索,当搜索到第 i 根火柴时,我们可以把它放到四组中的任意一种。对于每一种放置方法,我们继续对第 i + 1 根火柴进行递归搜索。当我们搜索完全部的 N 根火柴后,再判断每一组火柴的长度之和是否都相同。
dfs要注意剪枝细节,否则会超时:
在进行搜索之前,我们可以将火柴的长度从大到小进行排序,方便我们先搜索较长的火柴。
例如当火柴的长度为 [4,4,4,8] 时,每条边的长度为 5,如果我们先搜索长度为 8 的火柴,就可以发现它无法被放在任意一组中,因此直接退出搜索返回 False。
- 时间复杂度:O(4^N ),其中 N 是火柴的数量。
- 空间复杂度:O(N)。
class Solution {
public:
int target; // 目标边长
int bian[4];
// vector<int> flag;
bool makesquare(vector<int>& nums) {
if(nums.size()<=3)
return false; //剪枝1
int length = 0;
for(int i=0; i<nums.size(); i++){
length += nums[i];
}
if( length%4 ) //剪枝2
return false;
target = length/4;
//排序细节
// sort(nums.begin(), nums.end()); //升序排序会超时!!
sort(nums.rbegin(), nums.rend()); //降序排序(反向迭代器)
for(int i=0;i<4;i++) bian[i] = 0;
return dfs(nums,0);
}
bool dfs(vector<int>& nums, int index){
if( index == nums.size() ){
return target==bian[0] && target==bian[1] && target==bian[2] && target==bian[3];
}
int val = nums[index];
if(val > target)
return false; //剪枝3
//在四条边中找位置
for(int i=0; i<4; i++){
if( (bian[i]+val) > target )
continue;
bian[i] += val; //执行操作
//回溯 注意这里不是直接返回啊!!!如果false还要去还原操作再次尝试的
// return dfs(nums, index+1);
if( dfs(nums, index+1) )
return true;
bian[i] -= val; //还原操作
}
return false;
}
};