一、 题目
1. 题目描述
你将得到一个整数数组 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
- 位运算
- 数组
- 动态规划
- 回溯
- 状态压缩
- 👍 358
- 👎 0
2. 原题链接
链接: 473. 火柴拼正方形
二、 解题报告
1. 思路分析
这题是20220601儿童节每日一题。
求出总边长除4就是每条边长side,然后设置4个盒子,往里扔火柴就行了。
直接DFS回溯枚举,然后TLE了,想了一下 n=15 ,复杂度O(4n)= 2^30 是得TLE。
1) 所以进行了一个简单剪枝,放每个盒子的时候,如果这个盒子长度大于side就可以剪掉;那么可以先倒序排序,让长火柴先放,这样可以更快进入剪枝。3248ms过了,
2)后来我又剪了一下:先枚举用所有15根火柴能拼出来的所有长度O(2^n),用set记下来,n=15的时候是3.2w多次,就还好。然后放每根火柴的时候,如果这个盒子剩余能放的长度不在set里,说明用这15根火柴并不能拼出要求的长度,这个方案一定失败,可以剪掉。1124ms。
3) 追加:后来又剪了一下,把2剪枝去掉了,毕竟2^n,每个数尝试放盒子的时候检查是否前边有一样长度的盒子,如果有就不用放了,因为之前已经尝试过。
2. 复杂度分析
最坏时间复杂度O(4^n)
3. 代码实现
递归回溯枚举
。
class Solution:
def makesquare(self, matchsticks: List[int]) -> bool:
n = len(matchsticks)
if n < 4:
return False
s = sum(matchsticks)
side = s // 4
if side * 4 != s:
return False
matchsticks.sort(reverse=True)
res = [0] * 4
# 枚举每个数字是否出现能组合成什么值,2^n;这个组合值用于下边dfs的剪枝:
# 如果放火柴之后,本边剩余长度不能用火柴组合起来,那直接剪掉
# 本来我就是试试,没想到剪成功了!
# cmb = set()
# def dfs_sum(i,s):
# if i == n:
# cmb.add(s)
# return
# dfs_sum(i+1,s)
# dfs_sum(i+1,s+matchsticks[i])
# dfs_sum(0,0)
# print(cmb)
def dfs(i):
if i == n:
return True
# return all(res[j]==side for j in range(4))
for j in range(4):
# if res[j]+matchsticks[i]> side or side - res[j]-matchsticks[i] not in cmb :
if res[j]+matchsticks[i]> side :
continue
if j > 0 and res[j] == res[j-1]:
continue
if j > 1 and res[j] == res[j-2]:
continue
if j > 3 and res[j] == res[j-3]:
continue
res[j] += matchsticks[i]
if dfs(i+1):
return True
res[j] -= matchsticks[i]
return False
return dfs(0)
三、 本题小结
- 枚举型递归还是要算一下复杂度,不剪枝基本难过
链接: 698. 划分为k个相等的子集