「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战」
题目
还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。
输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。
示例1
输入: [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
复制代码
示例2
输入: [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
复制代码
题解
递归 + 回溯
满足所有的火柴拼成正方形,
- 需要所有火柴长度之和大于 且可以被 均分;
- 火柴数量必须大于等于 ;
如果不满足以上条件直接返回 ,符合条件的继续筛选
因为正方形又4个边,组成正方形需要 个相同边,所以需要将所有火柴长度之和平均分为 组且每组火柴长度之和相等;
利用背包思想,申请 背包;背包的容量是: ;所有的火柴依次放入背包中,如果可以正好放满 个背包,返回
用代码表示 ; 个背包,每个背包初始装了 个火柴长度
从左到右枚举每一根火柴,对任意位置 根火柴,可以放在4个背包中满足当火柴放入背包不超过背包容量的任意一个背包中。然后 试着将第 位置的火柴放入背包。
如果枚举完成所有火柴,且背包是满的 证明所有火柴可以组成一个正方形;
优化点
- 为了防止较深的递归,应该从数组中最大的火柴枚举,所以在枚举所有火柴之前对火柴进行递减排序
- 不必每次枚举4个背包,可以优先放置一个背包,尽可能先将第一个背包装满
完整代码
var makesquare = function (matchsticks) {
const t = matchsticks.reduce((a, b) => a + b);
//console.log('t', t);
if (t % 4 !== 0 || t === 0) return false;
const len = matchsticks.length;
matchsticks.sort((a, b) => b - a);
if (len < 4) return false;
const target = t / 4;
let list = [0, 0, 0, 0];
return dfs(0);
function dfs(idx) {
if (idx === len) {
return list.filter((v) => v === target).length === 4;
}
for (let i = 0; i < 4; i++) {
if (
list[i] + matchsticks[idx] > target ||
(i > 0 && list[i] === list[i - 1])
)
continue;
list[i] += matchsticks[idx];
if (dfs(idx + 1)) return true;
list[i] -= matchsticks[idx];
}
return false;
}
};
复制代码