题目描述
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2] 输出: true 解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: [3,3,3,3,4] 输出: false 解释: 不能用所有火柴拼成一个正方形。
注意:
- 给定的火柴长度和在
0
到10^9
之间。 - 火柴数组的长度不超过15。
问题分析
此题用DFS。先算出正方形每个边应该的长度target,然后将给定数组nums由大到小排序,一会儿火柴是按照由长到短的顺序放置于4个边中的。然后创建一个含有4个元素的数组sides,初始值为0,里面保存着已经放置在4个边里的火柴。变量position代表接下来该放置哪根火柴,初始值为0。接下来我们调用dfs函数:dfs(0, sides, target, nums),该函数返回以当前传入参数的状态下,接下来继续放置火柴最终能否使数组sides中的各元素都等于target。在dfs函数中,我们把接下来要放置的nums[position]这根火柴依次放置于4个边中,想放置于某一边先判断如果放进去那么边长是否会超过target,如果会超过,那么继续试下一个边;如果没超过,那么将该火柴放入这个边中(更新sides[i]),然后我们以当前状态调用dfs函数,如果dfs返回true,那么说明接下来继续放置火柴最终能使数组sides中的各元素都等于target,所以我们返回true。如果没返回true,说明将该火柴放入sides[i]是行不通的,所以我们接下来打算换个边放入试试,在这之前,我们要把这根火柴从这个边拿出来(sides[i] -= nums[position])。如果这根火柴放到哪个边里都行不通,那么我们就返回false。递归基是当全部火柴都已放置进了各边中时,这时各个边的长度一定都等于target,所以返回true。
代码实现
class Solution {
public:
bool makesquare(vector<int>& nums) {
if(nums.size() < 4)
return false;
int sum = 0;
for(int i : nums)
sum += i;
if(sum % 4 != 0)
return false;
sort(nums.rbegin(), nums.rend());
int target = sum / 4;
vector<int> sides(4, 0);
return dfs(0, sides, target, nums);
}
bool dfs(int position, vector<int>& sides, int& target, vector<int>& nums){
if(position == nums.size())
return true;
for(int i = 0; i < 4; i++){
if(sides[i] + nums[position] > target)
continue;
sides[i] += nums[position];
if(dfs(position + 1, sides, target, nums))
return true;
sides[i] -= nums[position];
}
return false;
}
};