698. 划分为 K 个总和相等的子集
class Solution:
# 回溯法
def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
s = sum(nums)
if s % k != 0: # 数组总和除以k除不尽,返回False
return False
self.target = s // k
for n in nums: # 若数组中存在一个数大于每份之和,返回False
if n > self.target:
return False
nums.sort()
visited = [0] * len(nums)
def backtrack(nums, k, visited, begin, temp):
if temp == 0: # 若当前的总和已经达到target,则继续寻找下一个子集(k-1)
return backtrack(nums, k-1, visited, 0, self.target)
if k == 0: # k份都分完了,则成功
return True
for i in range(begin, len(nums)):
if visited[i] == 1: # 若当前值已经用过,则继续
continue
if nums[i] > temp: # 若某一个值大于当前的和,则失败
break
visited[i] = 1 # 标记当前值为已使用
temp -= nums[i] # 去掉当前值后,对剩余值进行回溯
if backtrack(nums, k, visited, i+1, temp):
return True
visited[i] = 0 # 回溯完成,则需要加回去还原
temp += nums[i]
return False
return backtrack(nums, k, visited, 0, self.target)
- 升级版:存在负数的情况
class Solution:
def canPartitionKSubsets(self, nums, k):
# 划分子集的目标值
target, rem = divmod(sum(nums), k)
# 如果不能整除,可以直接返回
if rem: return False
# 递归调用,判断是否可以用当前的nums数组去凑到k个target
# 注意,这里有递归调用,再次到这里的时候,groups还是那个groups,但是nums不是当初的nums了
def search(groups):
# 整个nums数组已经全用完了,还没有从递归中return上来,且groups里的每组数都等于target
# 说明可以完成这个任务,所以return True
if not nums and all(map(lambda g : True if g == target else False,groups)):
return True
# 整个nums数组已经全用完了,还没有从递归中return上来,但groups里的每组数不完全等于target
# 说明不能完成这个任务,所以return False
if not nums and not all(map(lambda g : True if g == target else False,groups)):
return False
# 判断放到哪个组里面
for i, group in enumerate(groups):
## 取出末尾并删除1个元素
v = nums.pop()
## 如果group(上一层group)等于target,则找到一组,继续找下一组。必须把v放回去原来的数组
if group == target:
nums.append(v)
continue
# 把这个数放到这个组里
groups[i] += v
# 继续判断后续能否完成任务,如果可以,直接返回True,这会在递归中一路返回上去
# 在这里递归找到v应该放到哪个组里面
if search(groups): return True
# 否则,这个数不应该放到这个组里,把v从这个组里面拿出来,继续尝试其他的组。
# 由于上面已经将v从nums取出,故要将v放回nums
groups[i] -= v
nums.append(v)
if not group:
# print("break ... ")
break
# 并且返回False,表示这次回溯是失败的,这个v没有找到对应位置
return False
# 如果有相等的,那么相等的必然自己成组,也不需要判断,从nums中去除,并且减少组的数目
for index in range(len(nums) - 1,-1,-1):
v = nums[index]
if v == target:
nums.pop(index)
k -= 1
nums.sort() # 排序,有助于快速拿到结果
return search([0] * k)
473. 火柴拼正方形
class Solution:
def makesquare(self, matchsticks: List[int]) -> bool:
totalLen = sum(matchsticks)
if totalLen % 4:
return False
def dfs(idx: int) -> bool:
if idx == len(matchsticks):
return True
for i in range(4):
edges[i] += matchsticks[idx]
if edges[i] <= totalLen // 4 and dfs(idx + 1):
return True
edges[i] -= matchsticks[idx]
return False
matchsticks.sort(reverse=True)
edges = [0] * 4
return dfs(0)