【无标题】

动态规划是一种解决最优化问题的算法思想,通过分解问题并存储子问题的解来避免重复计算。文章介绍了动态规划的三个关键特点:最优子结构、重叠子问题和状态转移方程,并通过最长递增子序列和最大子数组和的例子展示了如何设计状态、找到状态转移方程及设置边界条件来解题。
摘要由CSDN通过智能技术生成

动态规划(Dynamic Programming, DP)是一种以求解最优化问题为目标的算法思想,它是将一个大问题划分成若干个子问题,并逐个求解子问题的过程。可以看做是递推式的扩展,其基础思想就是将问题拆分为更小的子问题来求解,然后将解组合起来得到原问题的解。

动态规划和递归求解都是分治法的一种变形,它们之间的区别在于动态规划保存了过程中的一些状态,避免了重复计算,提高了效率。

动态规划的特点

动态规划有三个重要的特点:

1. 最优子结构

(理解为氪分为几个子函数一样的分而治之)

最优子结构是指问题的最优解可以通过子问题的最优解推导出来。如果问题不具备最优子结构,则不能使用动态规划求解,需要使用其他算法思想。具有最优子结构的问题有很多,例如背包问题、图的最短路径问题等。

2. 重叠子问题

(理解为氪用一个最终,最简递推式来进行反复求最佳值)

重叠子问题是指在递归过程中,子问题被多次重复计算。针对这种情况,动态规划算法通过记忆化搜索或者 Bottom-up 的方式来避免重复计算,提高效率。

Bottom-up :是一种动态规划算法的实现方式,也称为自底向上法。它与 Top-down(自顶向下法,即记忆化搜索)是互补的思路,主要是针对具有重叠子问题和最优子结构特点的问题,通过递推的方式解决原问题。

3. 状态转移方程

状态转移方程是使用动态规划求解问题的核心。状态转移方程描述了子问题与原问题之间的关系,是从子问题的最优解推导出原问题的最优解的公式。通常需要分析问题的特点,设计出合适的状态表示方法,然后根据状态之间的转移关系得出状态转移方程,并用递推或者迭代的方式实现。

例如:

最长递增子序列问题的状态转移方程如下:

dp[i] = max{dp[j]+1},其中 0≤j<i 且 nums[j]<nums[i]

其中,nums 表示原始序列,dp[i] 表示以第 i 个元素为结尾的最长递增子序列的长度。这个方程的含义是,在前 i 个元素中,选择一个比第 i 个元素小的元素 j,然后将第 i 个元素接在第 j 个元素后面,形成一个新的递增子序列。如果存在多个这样的 j,则选择其中 dp[j] 最大的那一个。最终,dp[i] 的值为所有可行的 dp[j]+1 中的最大值。

动态规划的解题步骤

动态规划一般包括三个步骤:设计状态、找到状态转移方程、设置边界条件

例子1:最长递增子序列问题

最长递增子序列问题是一个经典的动态规划问题,其目标是在一个无序的序列中,求解最长的递增子序列。例如序列 [1, 3, 2, 4, 5, 7, 6] 的最长递增子序列是 [1, 2, 4, 5, 7],长度为 5。

要解决这个问题,需要分析其特点,确定状态和状态转移方程。

1. 设计状态

设 dp[i] 表示以第 i 个元素为结尾的最长递增子序列的长度。

2. 找到状态转移方程

对于第 i 个元素,如果前面存在比它小的元素 j,那么它可以接在第 j 个元素的后面形成一个新的递增子序列,此时 dp[i] 应该等于 dp[j]+1。如果不存在这样的 j,则 dp[i]=1,即以第 i 个元素结尾的最长递增子序列只包含第 i 个元素本身。

根据上述分析,可以得到如下状态转移方程:

int longest(int nums[], int n) 
{
    int dp[n];
    for (int i = 0; i < n; i++) {
        dp[i] = 1;   // 初始时,所有元素的最长递增子序列长度均为 1
    }
    int max = 1;
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[j] < nums[i]) {
                dp[i] = (dp[i] > dp[j]+1) ? dp[i] : dp[j]+1;
            }
        }
        max = (max > dp[i]) ? max : dp[i];
    }
    return max;
}

53. 最大子数组和

难度中等

6046

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]输出:6解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

示例 2:

输入:nums = [1]输出:1

示例 3:

输入:nums = [5,4,-1,7,8]输出:23

提示:

  • 1

5

  • -10

4

今天看到这个题,只能想到用双重循环进行一个遍历,显然是时间超限的。题解中提到的dp一下就清晰了很多,另开数组记录最优然后一次遍历即可解决问题。

int maxSubArray(int* nums, int numsSize)
{
    int max=0,temp=0;
    
    for(int i = 1 ; i < numsSize ; i++)
    {
        temp = 0;
        for(int j = i ; j > 0 ; j --)
        {
        temp += num[j];
        max = max > temp ? max : temp ;        
        }
        
     }

    return max;
}
int maxSubArray(int* nums, int numsSize)
{
    //用dp表示到每个位置时最大的子数组的值
    int dp[numsSize];

    //初始化
    dp[0] = nums[0];

    int num = dp[0];

    for(int i = 1 ; i < numsSize ; i++)
    {
        //dp[i]要么与前一个数组结合,形成新的连续子数组。要么自己为一个最大和子数组
        dp[i] = nums[i] > (dp[i-1] + nums[i]) ? num[i] : dp[i-1] + num[i];

        num=num>dp[i]?num:dp[i];
    }

    return num;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值