leetcode 473:火柴拼正方形 回溯

该博客讨论了一个编程问题,即如何使用所有给定的火柴棒拼成一个正方形。文章提供了两种解决方案:一是通过回溯法,先排除特殊情况,然后对火柴进行排序并尝试放置在四条边上;二是采用状态压缩和动态规划的方法,以更高效地解决这个问题。两种方法的时间复杂度和空间复杂度都有所不同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:
你将得到一个整数数组 matchsticks ,其中 matchsticks[i] 是第 i 个火柴棒的长度。你要用 所有的火柴棍 拼成一个正方形。你 不能折断 任何一根火柴棒,但你可以把它们连在一起,而且每根火柴棒必须 使用一次 。
如果你能使这个正方形,则返回 true ,否则返回 false 。
示例1:
在这里插入图片描述
输入: matchsticks = [1,1,2,2,2]
输出: true
解释: 能拼成一个边长为2的正方形,每边两根火柴。
示例 2:
输入: matchsticks = [3,3,3,3,4]
输出: false
解释: 不能用所有火柴拼成一个正方形。
思路1: 回溯的方式 类似于八皇后问题
因为要构成一个正方形且每根火柴都要使用,所以首先可以通过几种特殊情况,确定为false:
1)数组为空;数组长度小于4
2)数组求和之后除以4的值不是整数
排除以上情况之后,我们知道正方形有4条边,我们对每一根火柴在四条边中找位置,进行深搜,看是否所以火柴都能找到一个位置,由于数组可能很大,所以我们在深搜的时候,要注意时间不能超时。

class Solution:
    def makesquare(self, matchsticks: List[int]) -> bool:
        if not matchsticks or len(matchsticks) < 4:
            return False
        s = sum(matchsticks)
        if s % 4 != 0:
            return False
        l = len(matchsticks)
        self.bian = s / 4
        self.edge = [0] * 4  # 一共四条边,值为当前边的长度
        matchsticks.sort(reverse=True)  # 不排序将超时,排序的话可以减少一些递归的次数, 还要注意一定要是降序排列,将大的放到前面,可以一定程度减少递归的次数

        def dfs(number) -> bool:   # 这个函数可以放在所求函数里,也可以单独放一个函数
            if number == l:  # 所有的火柴都找到了自己的位置
                return True
            else:
                for i in range(4):  # 对四条边都尝试一下
                    self.edge[i] += matchsticks[number] 
                    if self.edge[i] <= self.bian and dfs(number+1):
                        return True
                    else:
                        self.edge[i] -= matchsticks[number]
                return False
        return dfs(0)  # 从第一个元素开始放

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

思路2:
官方题解中给出了一个状态压缩+动态规划的方式,可以减少时间复杂度,但是我看不懂。
在这里插入图片描述

class Solution:
    def makesquare(self, matchsticks: List[int]) -> bool:
        totalLen = sum(matchsticks)
        if totalLen % 4:
            return False
        tLen = totalLen // 4

        dp = [-1] * (1 << len(matchsticks))
        dp[0] = 0
        for s in range(1, len(dp)):
            for k, v in enumerate(matchsticks):
                if s & (1 << k) == 0:
                    continue
                s1 = s & ~(1 << k)
                if dp[s1] >= 0 and dp[s1] + v <= tLen:
                    dp[s] = (dp[s1] + v) % tLen
                    break
        return dp[-1] == 0

时间复杂度:O(n * 2 ^ n ) , 其中 nn 是火柴的数目。总共有 2^n个状态,计算每个状态都需要 O(n)。
空间复杂度:O(2^ n)。保存数组dp 需要 O(2^n) 的空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值