给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。
class Solution:
def integerBreak(self, n: int) -> int:
dp = [0]*(n+1)
dp[2] = 1
for i in range(3, n+1):
for j in range(1, i-1):
dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]))
return(dp[n])
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?
class Solution:
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
# 构造一个DP table
row = len(obstacleGrid)
col = len(obstacleGrid[0])
dp = [[0 for _ in range(col)] for _ in range(row)]
dp[0][0] = 1 if obstacleGrid[0][0] != 1 else 0
if dp[0][0] == 0: return 0 # 如果第一个格子就是障碍,return 0
# 第一行
for i in range(1, col):
if obstacleGrid[0][i] != 1:
dp[0][i] = dp[0][i-1]
# 第一列
for i in range(1, row):
if obstacleGrid[i][0] != 1:
dp[i][0] = dp[i-1][0]
print(dp)
for i in range(1, row):
for j in range(1, col):
if obstacleGrid[i][j] != 1:
dp[i][j] = dp[i-1][j] + dp[i][j-1]
return dp[-1][-1]
class Solution:
"""使用一维dp数组 """
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
m, n = len(obstacleGrid), len(obstacleGrid[0])
# 初始化dp数组
# 该数组缓存当前行
curr = [0] * n
for j in range(n):
if obstacleGrid[0][j] == 1:
break
curr[j] = 1
for i in range(1, m): # 从第二行开始
for j in range(n): # 从第一列开始,因为第一列可能有障碍物
# 有障碍物处无法通行,状态就设成0
if obstacleGrid[i][j] == 1:
curr[j] = 0
elif j > 0:
# 等价于
# dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
curr[j] = curr[j] + curr[j - 1]
# 隐含的状态更新
# dp[i][0] = dp[i - 1][0]
return curr[n - 1]
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
class Solution:
def numTrees(self, n: int) -> int:
dp = [0]*(n+1)
dp[0] = 1
for i in range(1, n+1):
for j in range(1, i+1):
# dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
# 以j为头结点左子树节点数量为 j-1
# 以j为头结点右子树节点数量为 i-j
dp[i] += dp[i-j] * dp[j-1]
return dp[n]
def test_1_wei_bag_problem():
weight = [1, 3, 4]
value = [15, 20, 30]
bag_weight = 4
# 初始化: 全为0
dp = [0] * (bag_weight + 1)
# 先遍历物品, 再遍历背包容量
for i in range(len(weight)):
for j in range(bag_weight, weight[i] - 1, -1):
# 递归公式
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
print(dp)
test_1_wei_bag_problem()
给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
class Solution:
def canPartition(self, nums: List[int]) -> bool:
taraget = sum(nums)
if taraget % 2 == 1: return False # 只有 奇+奇或者偶+偶的组合 都是偶数
taraget //= 2
dp = [0] * 10001
for i in range(len(nums)):
for j in range(taraget, nums[i] - 1, -1):
dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
return taraget == dp[taraget] ## key point 装满说明找到了
01背包问题是有 N 件物品和一个最多能被重量为 W 的背包,且每个物品只能使用一次。
背包的体积为sum / 2
背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值
背包如果正好装满,说明找到了总和为 sum / 2 的子集。
背包中每一个元素是不可重复放入。
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
class Solution:
def lastStoneWeightII(self, stones: List[int]) -> int:
dp = [0] * 15000
target = sum(stones) // 2
for i in range(len(stones)):
for j in range(target, stones[i]-1, -1):
dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
return sum(stones) - 2 * dp[target]
把m个同样的苹果放在n个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?注意:如果有7个苹果和3个盘子,(5,1,1)和(1,5,1)被视为是同一种分法。
'''
放苹果分为两种情况,一种是有盘子为空,一种是每个盘子上都有苹果。
令(m,n)表示将m个苹果放入n个盘子中的摆放方法总数。
1.假设有一个盘子为空,则(m,n)问题转化为将m个苹果放在n-1个盘子上,即求得(m,n-1)即可
2.假设所有盘子都装有苹果,则每个盘子上至少有一个苹果,即最多剩下m-n个苹果,问题转化为将m-n个苹果放到n个盘子上
即求(m-n,n)
'''
def f(m:int, n:int):
if m < 0 or n < 0:
return 0
elif m == 1 or n == 1:
return 1
else:
return f(m, n-1)+f(m-n,n)
while 1:
try:
num1, num2 = map(int, input().split())
print(f(num1, num2))
except:
break
请计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)从棋盘左上角出发沿着边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往上走。
def func(x, y):
if x < 0 or y < 0:
return 0
elif x == 0 or y == 0:
return 1
else:
return func(x-1, y) + func(x, y-1)
while 1:
try:
a,b = map(int, input().split())
c = func(a, b)
print(c)
except:
break
设计一种算法,打印n对括号的所有合法的(例如,开闭一一对应)组合。
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
res = []
def dfs(left, right, brackets):
if left > right:
return
if left == 0 and right == 0:
res.append(brackets)
if left > 0:
dfs(left - 1, right, brackets+'(')
if right >0:
dfs(left, right-1, brackets+')')
dfs(n,n,'')
return res
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
n = len(nums)
if n <= 1:
return n
max_ = 0
dp = [1] * n
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j]+1)
max_ = max(max_, dp[i])
return max_
class Solution:
def findLengthOfLCIS(self, nums: List[int]) -> int:
n = len(nums)
if n <= 1:
return n
max_ = 0
dp = [1] * n
for i in range(1, n):
if nums[i] > nums[i-1]:
dp[i] = dp[i-1] + 1
max_ = max(max_, dp[i])
return max_
给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。
class Solution:
def findLength(self, nums1: List[int], nums2: List[int]) -> int:
m, n = len(nums1), len(nums2)
dp = [[0 for _ in range(n+1)] for _ in range(m+1)]
max_ = 0
for i in range(1, (m+1)):
for j in range(1, (n+1)):
if nums1[i-1] == nums2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
max_ = max(max_, dp[i][j])
return max_
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列 的长度。如果不存在公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m, n = len(text1), len(text2)
dp = [[0 for _ in range(n+1)]for _ in range(m+1)]
max_ = 0
for i in range(1, m+1):
for j in range(1, n+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
max_ = max(max_, dp[i][j])
return max_
再求公共子数组(连续)或者子序列(不连续)时,关键在于处理遇到不同的字符时,如果是要求连续的那就不用管,不连续的时候得等于两个序列分别加上一个字符时的最大。
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
if len(nums) == 0:
return 0
dp = [0] * len(nums)
dp[0] = nums[0]
result = dp[0]
for i in range(1, len(nums)):
dp[i] = max(dp[i-1] + nums[i], nums[i])
result = max(result, dp[i])
return result
n = int(input())
nums = list(map(int, input().split()))
res = nums[0]
maxnum = minnum = nums[0]
if n == 1:
print(nums[0])
else:
for x in nums[1:]:
n1, n2 = x * maxnum, x * minnum
maxnum = max(n1, n2, x)
minnum = min(n1, n2, x)
if res < maxnum:
res = maxnum
print(res)
指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
while 1:
try:
s1, s2 = input(),input()
m,n = len(s1), len(s2)
dp = [[1 for i in range(n+1)] for j in range(m+1)]
for i in range(n+1):
dp[0][i] = i
for j in range(m+1):
dp[j][0] = j
for i in range(1, m+1):
for j in range(1, n+1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = min(dp[i-1][j-1],dp[i][j-1],dp[i-1][j])+1
print(dp[m][n])
except:
break
(1) dp[i][j] = dp[i-1][j-1] if word1[i-1] == word[j-1]
(2) dp[i][j] = dp[i-1][j-1] + 1 if word1[i-1] != word[j-1] and dp[i-1][j] == dp[i][j-1]
(3) dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1 if word1[i-1] != word[j-1] and dp[i-1][j] != dp[i][j-1]
对于1,如果新的字母是一样的,那就不用增加额外的操作
对于2,如果dp[i-1][j] == dp[i][j-1],说明新加入的两个字符可以通过替换的方式处理,只需要额外操作一次,所以dp[i][j]
= dp[i-1][j-1] + 1
对于3,如果dp[i-1][j] != dp[i][j-1],说明新加入的两个字符可以有其他的处理方式(删除,插入),所以dp[i][j]
= min(dp[i-1][j], dp[i][j-1]) + 1
给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。
s[i]与s[j]相等,s[i]与s[j]不相等这两种。
当不相等,一定是false。
当相等时,这就复杂一些了,有如下三种情况
情况一:下标i 与 j相同,同一个字符例如a,当然是回文子串
情况二:下标i 与 j相差为1,例如aa,也是文子串
情况三:下标:i 与 j相差大于1的时候,看区间 i + 1、j - 1 是否为回文。
class Solution:
def countSubstrings(self, s: str) -> int:
dp = [[False for _ in range(len(s)+1)]for _ in range(len(s)+1)]
cnt = 0
for i in range(len(s)-1, -1, -1): # notice here
for j in range(i, len(s)):
if s[i] == s[j] and (j - i <= 1 or dp[i+1][j-1]):
dp[i][j] = True
cnt += 1
return cnt
dp[i][j]:字符串s在[i, j]范围内最长的回文子序列的长度为dp[i][j]。
注意遍历的顺序
class Solution:
def longestPalindromeSubseq(self, s: str) -> int:
dp = [[0 for _ in range(len(s))] for _ in range(len(s))]
# 一个字符的回文子序列长度就是1
for i in range(len(s)):
dp[i][i] = 1
for i in range(len(s)-1, -1, -1): # notice here
for j in range(i+1, len(s)):
if s[i] == s[j]:
dp[i][j] = dp[i+1][j-1] + 2
else:
dp[i][j] = max(dp[i+1][j], dp[i][j-1])
return dp[0][-1]
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp0 = 0 # 一直不买
dp1 = -prices[0] # 只买了一次
dp2 = float('-inf') # 买了一次,卖了一次
for i in range(len(prices)):
dp1 = max(dp1, dp0-prices[i])
dp2 = max(dp2, dp1+prices[i])
return max(dp1, dp2)
在每一天,你可能会决定购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以购买它,然后在 同一天 出售。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp0 = 0 # have
dp1 = -prices[0] # no have
for i in range(len(prices)):
dp0 = max(dp0, dp1 + prices[i])
dp1 = max(dp1, dp0 - prices[i])
return max(dp0, dp1)
你最多可以完成 两笔 交易
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp0 = 0 # 一直不买
dp1 = -prices[0] # 买 1
dp2 = float('-inf') # 买 1 卖 1
dp3 = float('-inf') # 买 2 卖 1
dp4 = float('-inf') # 买 2 卖 2
for i in range(len(prices)):
dp1 = max(dp1, dp0-prices[i])
dp2 = max(dp2, dp1+prices[i])
dp3 = max(dp3, dp2-prices[i])
dp4 = max(dp4, dp3+prices[i])
return max(dp1, dp2, dp3, dp4)
最多可以完成 k 笔交易
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
n = len(prices)
if n < 2 or k == 0: return 0
k = min(k, n//2)
dp = [float('-inf')] * (2*k+1)
dp[0] = 0
dp[1] = -prices[0]
for i in range(1,n):
for j in range(1, 2*k+1):
if j & 1:
dp[j] = max(dp[j], dp[j-1]-prices[i])
else:
dp[j] = max(dp[j], dp[j-1]+prices[i])
return max(dp)
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if len(prices) < 2:
return 0
dp0 = 0 # donot have && no frozen
dp1 = float('-inf') # donot have && frozen
dp2 = -prices[0] # have
for i in range(1, len(prices)):
new_dp0 = max(dp0, dp1)
new_dp1 = dp2 + prices[i]
new_dp2 = max(dp2, dp0 - prices[i])
dp0, dp1, dp2 = new_dp0, new_dp1, new_dp2
return max(dp0, dp1)
# the maxvalue must be got from dp0&dp1 because they donnot have stock
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
dp0 = 0 # have
dp1 = -prices[0] # no have
for i in range(len(prices)):
dp0 = max(dp0, dp1 + prices[i] - fee)
dp1 = max(dp1, dp0 - prices[i])
return max(dp0, dp1)
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
class Solution:
def jump(self, nums: List[int]) -> int:
dp = [0] * 10000
# dp[i] 表示能跳i步最远能跳到哪里
dp[1] = nums[0]
n = len(nums)
if n == 1 or nums[0] == 0:
return 0
elif dp[1] >= n-1:
return 1
i = 2
while dp[i-1] < n-1:
max_ = 0
for j in range(dp[i-2]+1, dp[i-1]+1):
max_ = max(max_, j+nums[j])
dp[i] = max_
i += 1
return i-1
class Solution:
def maximalSquare(self, matrix: List[List[str]]) -> int:
col = len(matrix[0])
row = len(matrix)
res = 0
# dp[i][j]表示以第i行,第j列处为右下角的最大正方形的边长
dp = [[0 for _ in range(col+1)]for _ in range(row+1)]
for i in range(1, row+1):
for j in range(1, col+1):
if matrix[i-1][j-1] == '1':
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])+1
res = max(res, dp[i][j])
return res**2