class Solution:
def __init__(self):
self.ret = 0
self.l = []
def combine(self, n: int, k: int) -> List[List[int]]:
if n == 1:
return [[1]]
nums = [i for i in range(1,n+1)]
# print(nums)
start, path = 0,[]
self.backtrack(nums, k, start, path)
return self.l
def backtrack(self, nums , k, start, path):
if k == len(path):
self.ret+=1
self.l.append(path.copy())
return
if k < len(path):
return # 剪枝
for i in range(start, len(nums)):
# 天然排好序
# 选择->递归->撤销
path.append(nums[i])
self.backtrack(nums,k, i+1, path)
path.pop()
无重复元素的 整数数组 candidates 和目标值 target, 同一个数字可以 无限制重复被选取. 如果至少一个数字的被选数量不同, 则两种组合是不同的.
class Solution:
def __init__(self):
self.l = []
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
# candidates[i] >=2
# corner case
if target<=1 :
return self.l
start,path = 0,[]
candidates.sort() # 排序下否则递归时不好去重
# print(candidates)
self.backtrack(candidates, target, start, path)
return self.l
def backtrack(self, candidates, target, start, path):
if sum(path) == target:
self.l.append(path.copy())
return
if sum(path) > target: # 优化 candidates >=2 都是正数 剪枝
return
# 什么时候继续使用i ,什么时候使用i+1
for i in range(start, len(candidates)): # 允许重复使用当前元素就从start, 否则从start+1
# 优化去重
if i>0 and candidates[i] == candidates[i-1]:
continue
# 选择->递归->撤销选择
path.append(candidates[i])
self.backtrack(candidates,target,i,path)
path.pop()
区别不能用重复数字了
class Solution:
def __init__(self):
self.l = []
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
# 1<=candidates[i]<=50
candidates.sort() # 方便去重使用
start, path = 0, []
self.backtrack(candidates, target, path,start)
return self.l
def backtrack(self, candidates, target, path, start):
if sum(path) == target:
self.l.append(path.copy())
return
if sum(path) > target:
return
for i in range(start, len(candidates)):
# 不能包含重复的组合
if i>start and candidates[i] == candidates[i-1]:
continue
path.append(candidates[i])
self.backtrack(candidates,target,path,i+1) # 每个数字只用1次
path.pop()
感觉跟前面一样, 这有啥意义
class Solution:
def __init__(self):
self.l = []
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
# k个数, 总和n
nums = [i for i in range(1,10)]
# nums 有序
path,start = [],0
self.backtrack(nums, k, n, path, start)
return self.l
def backtrack(self, nums, k, n, path, start):
if len(path) ==k and sum(path) == n:
self.l.append(path.copy())
return # 一个结果
if sum(path) > n or len(path) >k:
return # 剪枝
for i in range(start, len(nums)):
# if i>start and nums[i] == nums[i-1]:
# continue
if nums[i] in path:
continue # 每个数字最多用一次
path.append(nums[i])
self.backtrack(nums,k,n,path,i+1) # 每个数字最多用一次
path.pop()
每个数字可以多次重复利用, TLE 了, On^target 换思路吧
class Solution:
def __init__(self):
self.ret = 0 # 返回个数
def combinationSum4(self, nums: List[int], target: int) -> int:
# nums 中元素无序, 且互不相同
nums.sort()
path,start = [],0
self.backtrack(nums, target, path, start)
return self.ret
def backtrack(self, nums, target, path, start):
if sum(path) > target:
return
if sum(path) == target:
self.ret +=1
return # 都是正数
for i in range(0 ,len(nums)): # 每个数字可重复使用
if sum(path) + nums[i] > target:
break
path.append(nums[i])
self.backtrack(nums, target, path,i ) # 可以重复使用当前元素
path.pop()
dp On*tartget
class Solution:
def __init__(self):
self.ret = 0 # 返回个数
def combinationSum4(self, nums: List[int], target: int) -> int:
# dp 是和为 target 的排列数
# dp[target] = dp[target-x] + dp[target]
# x 为nums 中的值
dp = [0] * (target+1)
dp[0] = 1 # target=0 是为0
for i in range(1,target+1):
for x in nums:
if x<=i:
dp[i] = dp[i] + dp[i-x]
# print(dp)
return dp[target]
dp, 回溯 啥时候用?
计数用dp, 穷举用回溯
用回溯 On^target
class Solution:
def __init__(self):
self.l = []
self.dict = {
"2" : ["a","b","c"],
"3" : ["d","e","f"],
"4" : ["g","h","i"],
"5" : ["j","k","l"],
"6" : ["m","n","o"],
"7" : ["p","q","r","s"],
"8" : ["t","u","v"],
"9" : ["w","x","y","z"]
}
def letterCombinations(self, digits: str) -> List[str]:
if len(digits) ==0:
return self.l
# digits.length = k
# digits[i] 对应的字母只能用一次
# 回溯穷举所有结果
start,path = 0,[]
self.backtrack(digits,start, path)
return self.l
def backtrack(self, digits, start, path):
if len(path) == len(digits):
self.l.append("".join(path.copy()))
return
if len(path) > len(digits):
return
# for d in digits:
for i in range(start,len(digits)): # 剪枝重复使用
d = digits[i]
for k in self.dict[d]:
path.append(k)
self.backtrack(digits, i+1, path)
path.pop()
class Solution:
def minimumPushes(self, word: str) -> int:
if len(word) < 8 :
return len(word)
# word 不同
n,m = len(word)//8,len(word)%8
l = [i*8 for i in range(1,n+1)]
ret = sum(l) + (m * (n+1))
# print(ret)
return ret