[路飞]_473.火柴拼正方形

「这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

473. 火柴拼正方形

题目

还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。

输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

示例1

输入: [1,1,2,2,2]
输出: true

解释: 能拼成一个边长为2的正方形,每边两根火柴。
复制代码

示例2

输入: [3,3,3,3,4]
输出: false

解释: 不能用所有火柴拼成一个正方形。
复制代码

题解

递归 + 回溯

满足所有的火柴拼成正方形,

  • 需要所有火柴长度之和大于 0 0 且可以被 4 4 均分;
  • 火柴数量必须大于等于 4 4

如果不满足以上条件直接返回 f a l s e false ,符合条件的继续筛选

因为正方形又4个边,组成正方形需要 4 4 个相同边,所以需要将所有火柴长度之和平均分为 4 4 组且每组火柴长度之和相等;

利用背包思想,申请 4 4个 背包;背包的容量是: 所有火柴长度之和 / 4 所有火柴长度之和/4 ;所有的火柴依次放入背包中,如果可以正好放满 4 4 个背包,返回 t r u e true

用代码表示 l i s t = [ 0 , 0 , 0 , 0 ] list= [0,0,0,0] ; 4 4 个背包,每个背包初始装了 0 0 个火柴长度

从左到右枚举每一根火柴,对任意位置 k k 根火柴,可以放在4个背包中满足当火柴放入背包不超过背包容量的任意一个背包中。然后 试着将第 k + 1 k+1 位置的火柴放入背包。

如果枚举完成所有火柴,且背包是满的 证明所有火柴可以组成一个正方形;

优化点

  • 为了防止较深的递归,应该从数组中最大的火柴枚举,所以在枚举所有火柴之前对火柴进行递减排序
  • 不必每次枚举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;
  }
};
复制代码

Guess you like

Origin juejin.im/post/7062595949033422878