LC 473. 火柴拼正方形 (opens new window) (opens new window)
中等
# 问题描述
你将得到一个整数数组 matchsticks
,其中 matchsticks[i]
是第 i
个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。
如果你能使这个正方形,则返回 true
,否则返回 false
。
示例 1:
输入: matchsticks = [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: matchsticks = [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
提示:
1 <= matchsticks.length <= 15
1 <= matchsticks[i] <= 108
# 回溯 + 剪枝
首先计算所有火柴的总长度 , 如果 不是 的倍数,那么肯定不能拼成正方形,返回 。
直接暴搜的话,时间复杂度为 ,需要考虑 剪枝优化。
- 从长到短枚举火柴长度,因为先选取较长的火柴,后面可以选择的空间会更少,从而减少查询次数。
- 对于当前边,如果加入第一根火柴时的递归就失败,可以进行剪枝,因为每根火柴棒必须 使用一次,买条边都是从长度 开始的,这条边失败,后面的边必然失败。
- 同理,对于加入最后一根火柴时的递归失败,也可以进行剪枝。
- 对于当前边,如果加入长度为 的火柴时,递归失败,那么,后面与 长度相同的火柴可以直接跳过。
- 当三条边已经满足要求时,第四条边必然满足要求。
/**
* @param {number[]} matchsticks
* @return {boolean}
*/
var makesquare = function (matchsticks) {
if (matchsticks.length < 4) return false
const sum = matchsticks.reduce((sum, cur) => sum + cur)
if (sum % 4 !== 0) return false
const target = sum / 4
matchsticks.sort((a, b) => b - a)
if (matchsticks[0] > target) return false
const visited = new Array(matchsticks.length).fill(false)
const backtrack = (len, edge) => {
if (len === target) {
len = 0
edge += 1
}
if (edge === 4) return true
for (let i = 0; i < matchsticks.length; i++) {
if (!visited[i] && len + matchsticks[i] <= target) {
visited[i] = true
if (backtrack(len + matchsticks[i], edge)) return true
visited[i] = false
if (!len) return false
if (len + matchsticks[i] == target) return false
while (i + 1 < matchsticks.length && matchsticks[i] === matchsticks[i + 1]) i++
}
}
return false
}
return backtrack(0, 1)
}
- 时间复杂度:
- 空间复杂度:
# 状态压缩 + 动态规划
前期的预处理是一样的,不同的是动态规划的过程。
使用状态 记录哪些火柴已经被选取( 的第 位为 表示第 根火柴已经被选,因此有 个状态), 表示当前未放满的边的长度。
- 当 时,所有火柴都未被选取,
- 当 时,记每条边长为 当前状态为( 的第 位为 ),第 根火柴未被选取的状态为 。若 且满足 ,那么 ,这里取余的意思是当前一条边放满时 ,那么 ,下一条未放满的边的长度为 。若不满足条件,则 ,表示状态 对应的火柴选取方式不可能按上述规则放入正方形的边。
最终,当所有火柴都已经被放入时(),如果 ,说明可以拼成正方形,否则不可以拼成正方形。
/**
* @param {number[]} matchsticks
* @return {boolean}
*/
var makesquare = function (matchsticks) {
if (matchsticks.length < 4) return false
const sum = matchsticks.reduce((sum, cur) => sum + cur)
if (sum % 4 !== 0) return false
const len = sum / 4
const n = matchsticks.length
const N = 1 << n
const dp = new Array(N).fill(-1)
dp[0] = 0
for (let s = 0; s < N; s++) {
for (let k = 0; k < n; k++) {
// 第 k 根火柴选中
if ((s >> k) & 1) {
const s1 = s & ~(1 << k)
if (dp[s1] >= 0 && dp[s1] + matchsticks[k] <= len) {
dp[s] = (dp[s1] + matchsticks[k]) % len
break
}
}
}
}
return dp[N - 1] === 0
}
- 时间复杂度:
- 空间复杂度:
上次更新: 2023/01/31 19:48:05
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 , 转载请注明出处!