算法进阶02---动态规划:

举例:

递归方法没有非递归方法快,因为递归中,子问题重复计算导致执行效率低

求 f(6)时需要求 f(5),f(4),f(3) ; 等到求 f(7)时,还需要再一次求 f(6), f(5),f(4),f(3)。

# 斐波那契数列
# 递归
def fibnacci(n):
    if n==1 or n==2:
        return 1
    else:
        return fibnacci(n-1)+fibnacci(n-2)

print(fibnacci(7))

# 非递归------比较快,因为递归的执行效率低,子问题的重复计算导致的
# 动态规划(DP)的思想 = 最优子结构,即递推式 + 重复子问题的存储(利用循环把需要的子问题存储)
def fibnacci_no_recurision(n):
    f = [0,1,1]   
    if n>2:
        for i in range(n-2):
            num = f[-1] + f[-2]
            f.append(num)
    return f[-1]      #reutrn f[n]
# 这里用队列更准确
print(fibnacci(7))

问题一:钢条切割问题

一共有  2的n-1次方  种。 

 需要得到递推式(1):

 钢条切割问题——最优子结构

自顶向下递归实现 :

# 递归推导式(1)
p1=[0,1,5,8,9,10,17,17,20,21,23,24,26,27,27,28,30,36,39,40,53]

def cut_rod_recurision_1(p,n):  #效率低
    if n==0:
        return 0
    else:
        res = p[n]
        for i in range(1,n):
            res = max(res,cut_rod_recurision_1(p,i)+cut_rod_recurision_1(p,n-i))
        return res

print(cut_rod_recurision_1(p1,20))

得到递推式(2):

# 递归推导式(2)
p1=[0,1,5,8,9,10,17,17,20,21,23,24,26,27,27,28,30,36,39,40,53]

def cut_rod_recurision_2(p,n):  # 效率高
    if n==0:
        return 0
    else:
        res=0
        for i in range(1,n+1):
            res = max(res,p[i] + cut_rod_recurision_2(p,n-i))
    return res  # 长度为n的钢铁,所有切割方案中的最优收益

print(cut_rod_recurision_2(p1,20))

 动态规划实现: 将递归的部分(重复求解子问题)保存起来。

p1=[0,1,5,8,9,10,17,17,20,21,23,24,26,27,27,28,30,36,39,40,53]

def cut_rod_dp(p,n):
    r = [0]
    for i in range(1,n+1):
        res = 0
        for j in range(1,i+1):
            res = max(res,p[j]+r[i-j])
        r.append(res)
    return r[n]

print(cut_rod_dp(p1,20))

 r[i] 表示最优切割收益;s[i] 表示左边不切割部分的长度

p1=[0,1,5,8,9,10,17,17,20,21,23,24,26,27,27,28,30,36,39,40,53]

def cut_rod_extend(p,n):
    r=[0]  # 最有收益
    s=[0]  # 最优收益情形中,左边不切割部分的长度
    for i in range(1,n+1):
        res_r = 0   # 价格最大值
        res_s = 0   # 价格最大值对应方案的左边不切割部分的长度
        for j in range(1,i+1):
            if p[j]+r[i-j] > res_r:
                res_r = p[j]+r[i-j]
                res_s = j
        r.append(res_r)
        s.append(res_s)
    return r[n] , s

def cut_rod_solution(p,n):
    r,s = cut_rod_extend(p,n)  # 得到最优收益和方案s
    ans = []   # 切割方案
    while n > 0:
        ans.append(s[n])
        n -= s[n]
    return ans

r,s = cut_rod_extend(p1,10)
print(s)   # 输出方案s[0, 1, 2, 3, 2, 2, 6, 1, 2, 3, 2],看不懂,如何直接输出结果
print(cut_rod_solution(p1,10))  # 给出切割方案

 最优化问题;先考虑是否为递归问题,如果是递归问题,那么很大程度上可以用动态规划解。

问题一:最长公共子序列(LCS)

 字符串相似度比对——模糊查找 ; 百度搜索一个问题

思考:暴力穷举法的时间复杂度时多少?  O( 2^(m+n) )

思考:最长公共子序列是否具有最优子结构性质?

 

c[i,j] 表示最长公共子序列的长度。 有了递推公式,可以使用递归,也可以用动态规划来实现,构造一个二维列表(m+1,n+1):

  • 最长公共子序列长度
  • 长度求解完成,但是怎么给出求解子序列——回溯法
# 求最长公共序列长度和箭头矩阵
def lcs(x,y):
    m=len(x)
    n=len(y)
    c=[[0 for _ in range(n+1)] for _ in range(m+1)]   # 公共子序列长度矩阵
    b=[[0 for _ in range(n+1)] for _ in range(m+1)]  # 1:左上方 2:左方  3:上方
    for i in range(1,m+1):
        for j in range(1,n+1):
            if x[i-1] == y[j-1]:
                c[i][j] = c[i-1][j-1]+1
                b[i][j] = 1  # 表示该位置来自 斜上方
            elif c[i-1][j] > c[i][j-1]:
                c[i][j] = c[i-1][j]
                b[i][j] = 3
            else:  # c[i-1][j] < c[i][j-1]
                c[i][j] = c[i][j-1]
                b[i][j] = 2
    for _ in c:
        print(_)
    print('------------')
    for _ in b:
        print(_)
    return c[m][n],b

# 求得最长公共子序列(回溯法)
def trackback(x,y):
    res=[]  # 保存公共序列的列表
    c,b=lcs(x,y)
    m=len(x)
    n=len(y)
    while m>0 and n>0:   # 回溯到第一列或第一行停止
        if b[m][n] == 1:  # 来自 左上方----->匹配
            res.append(x[m-1])    # 同一个字母在x中的下标小于矩阵的横轴
            m-=1
            n-=1
        elif b[m][n] ==2:  # 来自左边
            n-=1
        else:  #来自上方
            m-=1
    return ''.join(reversed(res))   # res里字母顺序反

print(trackback('ABCBDAB','BDCABA'))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值