python(6)动态规划

1.概念

(1)适用场景

一个问题的最优解,包含其子问题的最优解

(2)解题步骤

分析原问题最优解的结构特征

递归的定义最优值(状态转移函数),并关注初始条件、边界

计算最优的值,通常自底向上

综合计算信息,构造最优解

2.案例

(1)矿工

        1、问题描述:

                给定矿产n=5、矿工数量m=10、单个矿产钻石量G=[400,500,200,300,350]、单个矿产所需工人数L=[5,5,3,4,3]

        2、问题分析:

                2.1、分析原问题最优解的结构特征

                当前所获得钻石量为G[n],所用工人数L[n]

                要获得10个矿工挖掘第5座矿产的最优解F(5,10),需要在F(4,10)和F(4,10-L[4])+G[4]中取较大值

                2.2、建立递归关系,写出状态转移函数

                边界条件:当n=1,m≥L[0]时,F(n,m)=G[0];

                当n=1,m状态转移函数:F(n,m) = 0 (n

                F(n,m) = G[0] (n==1,m>=L[0])

                F(n,m) = F(n-1,m) (n>1,m

                F(n,m) = max(F(n-1,m), G[n-1]+F(n-1,m-L[n-1])) (n>1,m>=L[n-1])

                2.3、计算最优解的值

        

                采用自底向上的方式进行计算,像填表过程一样从左至右、从上到下逐渐获得计算结果。

                这样,可以不需要存储整个表格的内容,仅需要存储前一行的结果,就可以推出下一行的内容,避免了重复计算

                2.4、构造最优解

                回溯可得最优解得组合

        3、代码实现:

def goldmine(n, m, g, l):
    t_results = [0 for _ in range(m+1)]
    for i in range(1, m+1):
        if i < l[0]:
            t_results[i] = 0
        else:
            t_results[i] = g[0]
    for i in range(1, n):
        results = [0 for _ in range(m+1)]
        for j in range(1, m+1):
            if j < l[i]:
                results[j] = t_results[j]
            else:
                results[j] = max(t_results[j], g[i]+t_results[j-l[i]])
        t_results = results
    return results[-1]


print(goldmine(5, 10, [400, 500, 200, 300, 350], [5, 5, 3, 4, 3]))

(2)背包

        1、问题描述:

        背包承重量c=8,物品n[a,b,c,d]件,单个物品质量w[2,4,5,3],单个物品价值v[5,4,6,2]

        

        2、问题分析

                2.1、分析最优子结构性质

                前i个物品,能放进承重量为j的背包中,有两种情况:包含i,不包含i

                不包含i,最优价值是F(i-1,j)

                包含i,最优价值是v[i-1]+F(i-1,j-w[i-1])

                所以,在前i个物品中,最优价值是以上两种情况的较大值。

                2.2、分析递归关系,写出状态转移函数

                边界条件:F(0,j) = 0 (j>=0)

                F(i,0) = 0 (i>=0)

                状态转移函数:F(i,j) = F(i-1,j) (j-w[i-1]

                F(i,j) = max(F(i-1,j),v[i-1]+F(i-1,j-w[i-1])) (j-w[i-1]>=0)

                2.3、背包实例

                

                2.4、回溯

                若F(i,j)=F(i-1,j),则说明当前物品没有放到背包中,回溯到F(i-1,j)中

                若F(i,j)=vi+F(i-1,j-wi),则说明当前物品已经放到背包中,将该物品记录为组成最优解的元素,回溯到F(i-1,j-wi)

                重复上述回溯过程,直到i=0,即可获得所有组成最优解的物品集合

        3、代码实现

                3.1、计算最优记录表

                初始化记录表

                从上,从左到右,再到下,根据状态转移函数,计算记录表,最后返回表res

                3.2、回溯

                初始化物品是否包含的列表

                初始化已包含的物品,和质量

                自底向上,从右到左,条件循环,res[i][j] > res[i-1][j]时,修改物品是否包含的列表为True,同时,修改已包含的质量,修改已包含的物品,直到已包含的物品=0

                可得到,True就是最优解时已包含的物品

def backpack_record(n, c, w, v):
    backpack_rec = [[0 for _ in range(c+1)] for _ in range(len(n)+1)]
    for i in range(len(n)+1):
        for j in range(c+1):
            if j < w[i-1]:
                backpack_rec[i][j] = backpack_rec[i-1][j]
            else:
                backpack_rec[i][j] = max(backpack_rec[i-1][j], v[i-1]+backpack_rec[i-1][j-w[i-1]])
    return backpack_rec


def backpack_results(n, c, w, res):
    print("最大价值是:", res[len(n)][c])
    x = [False for _ in range(len(n)+1)]
    j = c
    i = len(n)
    while i >= 0:
        if res[i][j] > res[i-1][j]:
            x[i] = True
            j -= w[i-1]
        i -= 1
    for i in range(len(x)):
        if x[i]:
            print("第", i, "个", end=" ")


n = ['a', 'b', 'c', 'd']
c = 8
w = [2, 4, 5, 3]
v = [5, 4, 6, 2]
res = backpack_record(n, c, w, v)
backpack_results(n, c, w, res)

(3)最长递增子序列

        1、问题描述:

        给定一个数组,找出数组中长度最长的递增子序列

        2、问题分析

        设f(i)表示序列中以ai为末元素的最长递增子序列的长度,则在求以ai为末元素的最长递增子序列时,找到所有序号在i前面且小于ai的元素aj,即j

        如果这样的元素存在,那么对所有aj,都有一个以aj为末元素的最长递增子序列的长度f(j),把其中最大的f(j)选出来,那么f(i)就等于最大的f(j)加上1,

        即以ai为末元素的最长递增子序列,等于以使f(j)最大的那个aj为末元素的递增子序列最末再加上ai;

        如果这样的元素不存在,那么ai自身构成一个长度为1的以ai为末元素的递增子序列。

        3、代码实现

def test1(arr):
    """ 找到数组中,以每个数为结尾的递增子序列的长度,以list返回"""
    n = len(arr)
    list = [0] * n
    for i in range(n):
        list[i] = 1
        for j in range(i):
            if arr[i] > arr[j]:
                list[i] = max(list[i], list[j]+1)
    return list


def test2(arr):
    list = test1(arr)
    # 最长子序列的长度
    n = max(list)
    ans = [0] * n
    # 有最长子序列的末尾元素的索引
    index = list.index(n)
    n -= 1
    ans[n] = arr[index]
    for i in range(index, -1, -1):
        if arr[i] < arr[index] and list[i] == list[index] - 1:
            n -= 1
            ans[n] = arr[i]
            index = i
    return ans


arr = [3,1,4,5,9,2,6,5,0]
print(test2(arr))

(4)通配符匹配

        1、问题描述

        s为待匹配字符串,

        p为匹配模式,'?' 匹配任意单个字符串,'*'匹配任意长度字符串

        2、问题分析

        dp[i][j]为s中前i个字符,与p中前j个字符,是否匹配,

        状态转移函数:

                p[j] = ? or p[j] = s[i],dp[i][j] = dp[i-1][j-1]

                p[j] = *,dp[i][j] = dp[i-1][j] | dp[i][j-1]

        边界条件:

                s为空,p为空,则dp[0][0]=True

                s为空,p不为空,前j个都为‘*’,则dp[0][j]=True,否则False

                p为空,s不为空,则dp[i][0]=False

        3、代码实现

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m, n = len(s), len(p)
        dp = [[False] * (n+1) for _ in range(m+1)]
        dp[0][0] = True
        for j in range(1, n+1):
            if p[0:j] == '*' * j:
                dp[0][j] = True
            else:
                break
        
        for i in range(1, m+1):
            for j in range(1, n+1):
                if p[j-1] == '*':
                    dp[i][j] = dp[i][j-1] | dp[i-1][j]
                elif p[j-1] == '?' or p[j-1] == s[i-1]:
                    dp[i][j] = dp[i-1][j-1]
        
        return dp[m][n]

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值