根据在算法-动态规划2里提到的思路,让我们来解决一个组合问题 :leetcode 第77题。
0 问题描述
给定2个整数n,k,求出从[1,…,n]中取k个数字的所有组合。
例如 n=4, k=2
就是从[1,2,3,4]中取2个数字的所有组合。
结果为:[[1, 2], [1, 3], [2, 3], [1, 4], [2, 4], [3, 4]]
1 递归解法
(1)假设组合中不包含 第n个数字,原问题就变为:
求出从[1,…,n-1] 中取k个数字的所有组合。
(2)假设组合中包含 第n个数字,原问题就变成了:
求出从[1,…,n-1]中取 k-1 个数字的组合,然后中每个组合中,把第n个数字加进去。
(1)+ (2)就是问题的解。
import copy
class Solution:
def combine(self, n, k):
return self.combine_core(n, k)
def combine_core(self, n, k):
if n <= 0 or k <= 0:
return list()
if k == 1:
return [[i] for i in range(1, n + 1)]
# 第(1)种情况
res1 = self.combine_core(n - 1, k)
# 第(2)种情况
res2 = self.combine_core(n - 1, k - 1)
# 把 第n个数字 加入(2)的结果中
for l in res2:
l.append(n)
# (1)+(2)
res = list()
if res1:
res.extend(res1)
if res2:
res.extend(res2)
return res
# main
s = Solution()
print(s.combine(4, 2))
2 记忆化搜索解法
import copy
class Solution:
def __init__(self):
self.memo = list()
def combine(self, n, k):
self.memo = [[None for j in range(k+1)] for i in range(0, n+1)]
return self.combine_core(n, k)
def combine_core(self, n, k):
if n <= 0 or k <= 0:
return list()
if k == 1:
return [[i] for i in range(1, n + 1)]
# 返回存储的结果
if self.memo[n][k] is not None:
return self.memo[n][k]
res1 = self.combine_core(n - 1, k)
res2 = self.combine_core(n - 1, k - 1)
for l in res2:
l.append(n)
res = list()
if res1:
res.extend(res1)
if res2:
res.extend(res2)
# 存储结果,因为python的list赋值是对象引用的赋值,这里需要用深拷贝
self.memo[n][k] = copy.deepcopy(res)
return res
3 动态规划的解法
def combine(n, k):
memo = [[list() for j in range(k + 1)] for i in range(0, n + 1)]
for i in range(1, n+1):
memo[i][1] = [[v] for v in range(1, i + 1)]
for i in range(1, n+1):
for j in range(1, k+1):
res1 = memo[i - 1][j]
res2 = memo[i - 1][j - 1]
for l in res2:
l.append(i)
if not memo[i][j]:
memo[i][j].extend(copy.deepcopy(res1))
memo[i][j].extend(copy.deepcopy(res2))
return memo[n][k]
# main
print(combine(4, 2))
注意:
这个动态规划的解法,因为涉及到list的深拷贝,所以会比较耗时。在leetcode上当跑到(20,16)时,会因为耗时太长而通不过的。在前面提到的慕课网的课程里,是通过回溯法来解决的。我在这里,是借这个例子来实践前面学习到的动态规划的解题思路。