力扣每日一题0601-473. 火柴拼正方形

156 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。

如果你能使这个正方形,则返回 true ,否则返回 false 。

示例 1:

image.png

输入: matchsticks = [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。

示例 2:

输入: matchsticks = [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。

提示:

  • 1 <= matchsticks.length <= 15
  • 1 <= matchsticks[i] <= $10^8$

方法一:回溯

首先计算所有火柴的总长度 totalLen\textit{totalLen},如果 totalLen\textit{totalLen} 不是 4 的倍数,那么不可能拼成正方形,返回 false\text{false}。当 totalLen\textit{totalLen} 是 4 的倍数时,每条边的边长为 len=totalLen4\textit{len} = \dfrac{\textit{totalLen}}{4},用 edges\textit{edges} 来记录 4 条边已经放入的火柴总长度。对于第 index\textit{index} 火柴,尝试把它放入其中一条边内且满足放入后该边的火柴总长度不超过 len\textit{len},然后继续枚举第 index+1\textit{index} + 1 根火柴的放置情况,如果所有火柴都已经被放置,那么说明可以拼成正方形。

为了减少搜索量,需要对火柴长度从大到小进行排序。

var makesquare = function(matchsticks) {
    const totalLen = _.sum(matchsticks);
    if (totalLen % 4 !== 0) {
        return false;
    }
    matchsticks.sort((a, b) => a - b);
    for (let i = 0, j = matchsticks.length - 1; i < j; i++, j--) {
        const temp = matchsticks[i];
        matchsticks[i] = matchsticks[j];
        matchsticks[j] = temp;
    }

    const edges = new Array(4).fill(0);
    return dfs(0, matchsticks, edges, Math.floor(totalLen / 4));
}

const dfs = (index, matchsticks, edges, len) => {
    if (index === matchsticks.length) {
        return true;
    }
    for (let i = 0; i < edges.length; i++) {
        edges[i] += matchsticks[index];
        if (edges[i] <= len && dfs(index + 1, matchsticks, edges, len)) {
            return true;
        }
        edges[i] -= matchsticks[index];
    }
    return false;
};

复杂度分析

  • 时间复杂度:O(4n)O(4^n),其中 n 是火柴的数目。每根火柴都可以选择放在 4 条边上,因此时间复杂度为 O(4n)O(4^n)
  • 空间复杂度: O(n)O(n)。递归栈需要占用 O(n)O(n) 的空间。