动态规划初步背包、最长子序列、子序列和最大等

动态规划问题:

0-1 背包问题

例如:背包的最大容量为:4,有三种商品重量和价值分别为:
wight = [2,1,3]
val= [4,2,3]
确定价值最大
解析:
第一步状态选择:
状态:背包的容量和可选的物品 选择:装or 不装
第二步定义dp数组:
把状态用数组表示: dp[i,w] 定义:对于前i个商品,当前背包为w时,背包可以装的数量

import numpy as np
N,W = 3,4
wight = [2,1,3]
val= [4,2,3]
#dp[i,w] 表示在背包剩余w的情况下,前i个商品的最大价值
dp = np.zeros((N+1,W+1))
def bag(val,wight,dp):
    dp[0,:]= 0
    dp[:,0]= 0
    for i in range(1,N+1):
        for w in range(1,W+1):
            #如果剩余的背包值小于i个重量只能选择不装
            if w < wight[i-1]:
                dp[i,w]= dp[i-1,w]
            else: #装不装择优
                dp[i,w] = max(dp[i-1,w-wight[i-1]]+val[i-1],dp[i-1,w])
    return dp[-1,-1]

bag(val,wight ,dp)

#[[0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]
# [0. 0. 0. 0. 0.]]
#[[0. 0. 0. 0. 0.]
# [0. 0. 4. 4. 4.]
# [0. 2. 4. 6. 6.]
# [0. 2. 4. 6. 6.]]

#在一个数组中,选择不相邻的数使和为最大
分析:
第一步:状态:可选的数字 选择:选 or 不选
第二步:定义dp[i] 表示前i个数字的最大和
注意边界条件

#递归-带有备忘录的递归,解决了重叠子问题
arr = [1,2,4,1,7,8,3]
memo = {}
def rec_opt(arr,i):
    if i == 0:
        return arr[0]
    if i == 1:
        return max(arr[0],arr[1])
    if i in memo:
        return memo[i]
    else:
        A = rec_opt(arr,i-2)+arr[i]
        B = rec_opt(arr,i-1)
        memo[i] = max(A,B)
    return max(A,B)
rec_opt(arr,len(arr)-1) 

#非递归
#[4,1,1,9,1]
#arr = [4,1,1,9,1]
arr = [1,2,4,1,7,8,3]
dp = [0 for _ in range(len(arr))]
def dp_opt(arr):
    dp[0]=arr[0]
    dp[1]= max(arr[0],arr[1])
    for i in range(2,len(arr)):
        dp[i] = max(dp[i-2]+arr[i],dp[i-1])
    print(dp)
    return dp[-1]
dp_opt(arr)
#[1, 2, 5, 5, 12, 13, 15]

判断一个数组里是否存在元素子集之和为S,如果存在返回True
分析:
第一步:状态:前i可选数 和 数字之和S 选择:选 or 不选
第二步:定义dp[i] [s]表示前i个数字是否可以凑出S值
注意边界条件

#判断一个数组里是否存在元素之和为S,如果存在返回True
import numpy as np
arr = [3,34,4,12,5,2]
def subsetdp(arr,S):
    subset = np.zeros((len(arr),S+1),dtype= 'bool')
    subset[:,0] = True 
    subset[0,:] = False
    subset[0,arr[0]] = True 
    for i in range(1,len(arr)):
        for s in range(1,S+1):
            if s < arr[i]:
                subset[i,s] = subset[i-1,s]
            else:
                A = subset[i-1,s]
                B = subset[i-1,s-arr[i]]
                subset[i,s] = A or B
    return subset[-1,-1]

print(subsetdp(arr,9)) #True
print(subsetdp(arr,10)) #True
print(subsetdp(arr,11)) #True
print(subsetdp(arr,12)) #True
print(subsetdp(arr,13)) #False

最长公共子序列

最长公共子序列(Longest Common Subsequence,简称 LCS)是一道非常经典的面试题目,因为它的解法是典型的二维动态规划,大部分比较困难的字符串问题都和这个问题一个套路,比如说编辑距离。
题目就是让我们求两个字符串的 LCS 长度:
状态转移方程
在这里插入图片描述

输入: str1 = "abcde", str2 = "ace" 
输出: 3  
解释: 最长公共子序列是 "ace",它的长度是 3

分析:
第一步:状态:s1 和 s2 中的元素 选择:在 or 不在
第二步:定义dp[i][j]的含义是:对于s1[1…i]和s2[1…j],它们的 LCS 长度是dp[i][j]。
DP table:
在这里插入图片描述

def LongCommonSub(str1,str2):
    row,col = len(str1),len(str2)
    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 str1[i-1] ==str2[j-1]:
                dp[i][j] = dp[i-1][j-1] +1 
            else:
                dp[i][j] = max(dp[i][j-1],dp[i-1][j])   
    return dp[-1][-1]

str1 = "abcde"
str2 = "ace" 
res = LongCommonSub(str1,str2)
print(res) #3


求一个序列的回文,

在求“这个序列”和“这个序列的倒序列”的最长公共子序列。其中,回文不一定是连续的,最长公共子序列也不一定是连续的。

def LongCommonSub(str1,str2):
    row,col = len(str1),len(str2)
    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 str1[i-1] ==str2[j-1]:
                dp[i][j] = dp[i-1][j-1] +1
            else:
                dp[i][j] = max(dp[i][j-1],dp[i-1][j])
    return dp[-1][-1]
import sys
if __name__ == '__main__':
    while True:
        line = sys.stdin.readline().strip()
        lens = len(line)
        if not line:
            break
        lcs = LongCommonSub(line,line[::-1])
        print(lens-lcs)

最长公共子串(最长回文子串 )

动态规划状态转移方程式在这里插入图片描述

class Solution:
    def longestPalindrome(self, s: str) -> str:
        '''
        #动态规划
        rever_s = s[::-1]
        max_len,max_index = 0, 0 
        dp = [[0 for _ in range(len(s)+1)] for _ in range(len(s)+1)]
        for i in range(1,len(s)+1):
            for j in range(1,len(s)+1):
                if rever_s[i-1] == s[j-1]:
                    dp[i][j] = dp[i-1][j-1]+1
                    if dp[i][j] >max_len:
                        #判断比较的字符是否来自同一个字符串
                        prei = len(s) - i
                        nowi = prei + dp[i][j] 
                        if nowi == j:
                            max_len = dp[i][j]
                            max_index = j - max_len
                else:
                    dp[i][j] = 0
        return s[max_index:max_index+max_len]
        '''
        #中心扩展法:
        max_len,max_index = 0,0
        for i in range(len(s)):
            left = self.expand(s,i,i)
            right = self.expand(s,i,i+1)
            max_ = max(left,right)
            if max_>max_len:
                max_len=max_
                max_index = i-(max_len-1)//2
        return s[max_index:max_index+max_len]

    def expand(self,str1,left,right):
        while left >=0 and right <len(str1) and str1[left] == str1[right]:
            left -=1
            right +=1
        return right - left -1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值