剑指offer:动态规划

动态规划

记忆化搜索,把已经计算过的内容存下来,不需要重复计算。(斐波那契数列)自上而下。

memo = [-1] * (n + 1)
def f2(n):
	if n == 0:
		return 0
	if n == 1:
		return 1
	if memo[n] == -1:
		memo[n] = f2(n - 1) + f2(n - 2)
	return memo[n]

动态规划:将原问题拆解成若干子问题,同时保存子问题的答案,使得每个子问题只求解一次,最终获得原问题的答案。斐波那契数列是自上而下思考问题的方式,先考虑f(5),然后到f(4)…f(0),而动态规划是自下而上的思考方式,先考虑f(1),f(2)…然后f(5)。

def f3(n):
	memo = [-1] * (n + 1)
	for i in range(0,n + 1):
		if i == 0:
			memo[i] = 0
		elif i == 1:
			memo[i] = 1
		else:
			memo[i] = memo[i - 1] + memo[i - 2]
	return memo[n]

动态规划常常适用于有重叠子问题和最优子结构性质的问题,所以很多时候动态规划问题也是一个递归问题。
状态:递归函数的定义
状态转移方程:根据当前的状态,推断出下一个状态的方程。
通常把它转换为一个二维数组的问题。

问题:
有个小偷偷东西,他的背包容量是C,物品的重量和价值对应,请问小偷应该怎么偷东西才能获得最大价值的东西。
在这里插入图片描述
状态:定义一个状态p(i,w),该状态表示加入第i个物品,在背包容量为w的时候的解,并且我们把最优解定义为m(i,w)
状态转移方程:
当加入一个新的物品i,在容量为w的情况下,它无非有两种可能性:
1.当前的物品可以加入进来m(i,w)=m(i-1,w-wi)+Vi
2.不把当前物品加入进来:m(i,w)=m(i-1,w)
状态转移方程:
在这里插入图片描述
横轴是背包容量,纵轴是物体(每个物体只有一个,每一次拿一个物品放进背包)
在这里插入图片描述
右下角就是最终值。
题目:
给定一个包含正数的非空数组,是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

找到一些正整数,每个数只能用一次,使得这些数的和等于整个数组元素的和的一半。背包的容量就是数组和的一半,每个数就是要添进背包里的值。

状态:
定义dp[i][j]为[0,i]个物品中挑选若干个,能否使得这些数字和等于j,其中i表示当前步骤拿进来的数字,j也就是正整数之和
状态转移方程:
当前数字比正整数之和的一半大,则保持同一列上一次的状态。nums[i] > j:dp[i][j] = dp[i - 1][j]
当前数字比正整数之和的一半小的时候,就要看加上上一个数满不满足条件,用[j - nums[i]]找到上一个数的状态,依次类推就把大问题转化为小问题了。
nums[i] < = j :dp[i][j] = dp[i - 1][j] or dp[i - 1][j - nums[i]]
在这里插入图片描述

def canPartition(self,nums):
	nums_sum = sum(nums)
	if nums_sum % 2 !=0:
		return False
	c = nums_sum // 2
	dp = [[0]*(c + 1) for _ in range(lem(nums))]
	#对第一列数据进行处理全为1,方便后面j - num时该数字刚好是数组和的一半
	for i in range(len(nums)):
		dp[i][0] = 1
	#对放进来的第一个数进行处理,因为它满足条件
	if nums[0] <= c:
		dp[0][nums[0]] = 1
	#第一个for循环控制列
	for i in range(1,len(nums)):
		num = nums[i]
		#第二个for循环控制行
		for j in range(1,c + 1):
			if num > j:
				dp[i][j] = dp[i - 1][j]
				continue
			dp[i][j] = dp[i - 1][j] or dp[i - 1][j - num]
		return dp[-1][-1]

题目:
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

因为指数增长的速度是最快的,但是3 * 3比2 * 2 * 2大,3 * 1 比2 * 2 要小,所以我们只需要找尽可能多的3相乘,然后剩下的用2相乘就行了,但是最后不能是3 * 1。

class Solution:
    def cutRope(self, number):
        # write code here
        if number == 1:
            return 1
        if number == 2:
            return 2
        if number == 3:
            return 3
        three = number // 3
        if three * 3 + 1 == number:
            three -= 1
        two = (number - three * 3) // 2
        return pow(3,three)*pow(2,two)

最长公共子序列
题目:
给定两个字符串text1和text2,返回这两个字符串的最长公共子序列(子序列中字母的相对位置不能发生改变)

状态:定义dp[i][j],表示s1的前i个字符和s2的前j个字符最长公共子序列的大小。
状态转移方程:
1.当S1[i] == S2[j]:dp[i][j] = dp[i-1][j-1] + 1,当遇到两个字符串中的字符相同的时候,取上一个两个字符串都没有加入的时候的状态加1,因为这时候的状态就是没有引入新的字母的时候的最大公共子序列。
在这里插入图片描述
此时的右下角最后一个的位置,就是由e行d列的这个数(不加入e时的最大子序列)加1得到。
2.当S1[i] != S2[j]:dp[i][j] = max(dp[i - 1][j]),dp[i][j-1])
在纵向如果找到相同的会加1,在上面的一行找到了也会加1,所以不同的时候要去它相对位置上面和左边的最大值。
在这里插入图片描述

def solution(text1,text2):
	#防止最后一个越界了算不到。所以直接加一位
	dp = [[0] * (len*(text2) + 1) for _ in range(len(text1) + 1)]
	for i in range(1,len(text1) + 1):
		for j in range(1,len(text1) + 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])
	return dp[-1][-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值