题目
题目来源:力扣(LeetCode
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例 1:
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
思路分析
深度优先搜索 + 回溯
- 将给定的火柴进行分组,将他们分成四组,每一根火柴恰好属于其中一组。
- 每一组火柴的长度之和都相同,等于所有火柴长度之和的四分之一。
- 用深度优先搜索将所有的分组情况都枚举出来,对于每一种情况,判断一下是否满足上面的两种情况。
- 依次对每一根火柴进行搜索,当搜索出第 i 根火柴的时候,我们可以把它放到四组中的任意一组。
- 对于每一根火柴的放置方法,我们继续对第 i + 1 根火柴进行递归搜索。
- 当我们搜索完全部的 N 根火柴后,再判断每一组火柴的长度之和是否都相同。
var makesquare = function(matchsticks) {// 判断数组是否存在且长度不为 0if (matchsticks === null || matchsticks.length == 0) return false;// 火柴的总长度let allLen = 0;for (let item of matchsticks) {allLen += item}// 判断所有火柴是否可以刚好围成正方形if (allLen % 4 !== 0) return false;// 正方形的边长const sideLen = allLen / 4;// 将火柴数组从大到小排序,方便之后优化matchsticks.sort((a, b) => b - a);// 边长数组,分别保存正方形 4 条边的长度let sideList = new Array(4);// 赋初值for (let i = 0; i < sideList.length; i++) {sideList[i] = 0;}// index 表示访问到当前火柴的位置const dfs = (index) => {// 如果火柴都访问完了,判断四条边长是否相等,如果相等,说明是正方形,返回trueif (index == matchsticks.length) {return (sideList[0] === sideList[1] && sideList[1] === sideList[2] && sideList[2] === sideList[3])}// 当前正在处理的火柴const targetLen = matchsticks[index];// 因为刚才对火柴进行了排序,所以如果有火柴的长度targetLen 大于 我们所计算的边长sideLen,说明所有火柴不能拼接成一个正方形,返回 falseif (targetLen > sideLen) return false;// 到这一步,说明火柴还没访问完for (let i = 0; i < 4; i++) {// 这一步是回溯,先加上,再去递归判断下一步,如果false,则减去if (sideList[i] + targetLen <= sideLen) {// 如果当前火柴放到 sideList[i] 这个边上,长度不大于 边长 sideLen,我们就放到这条边上sideList[i] += targetLen;if (dfs(index + 1)) {return true;}// 如果当前火柴放到 sideList[i] 这个边上,最终不能构成正方形,我们就把他从 sideList[i] 这个边上给移除,然后再试其他的边sideList[i] -= targetLen}}//如果不能构成正方形,直接返回falsereturn false;}return dfs(0)};
