动态规划应用篇:一维动态规划问题

简单回顾一下,动态规划算法的核心思想在于通过将复杂问题分解为多个相互重叠的子问题,构建最优解与这些子问题之间的递推关系,从而避免重复计算,高效地得出全局最优解,适用于求解具有最优子结构和重叠子问题特性的最优化问题。一般求解步骤可分为:

1. 定义状态

2. 建立状态转移方程

3. 确定边界条件

4. 求解最优解或最优值。

现在,我们继续深入学习动态规划算法的应用。一维动态规划问题通常涉及到一维数组或序列,并且需要我们通过定义合适的状态、状态转移方程以及边界条件来解决。在本篇文章中,我们将聚焦于解决两个经典的一维动态规划问题:最长递增子序列和最大子序和问题。

最长递增子序列问题

最长递增子序列是指在一个序列中,找到一个子序列,使得子序列中的元素是递增排列的,并且这个子序列的长度尽可能地长。如[1, 5, 2, 3, 8, 4, 7]的最长递增子序列为[1, 2, 3, 4, 7],长度为 5。本题只要求出最长长度即可。

1.状态定义:

为了解决最长递增子序列问题,我们需要定义合适的状态。在这里,我们可以使用一维数组dp来表示以每个元素结尾的最长递增子序列的长度。假设dp[i]表示以第i个元素结尾的最长递增子序列的长度

2.状态转移方程:

nums数组用于存储原序列。对于dp[i],我们希望找到以第 i 个元素结尾的最长递增子序列的长度,就要考虑在元素 i 之前的元素 j。如果 nums[i] 大于 nums[j],那么 nums[i] 可以接在 nums[j] 后面形成一个更长的递增子序列。遍历所有符合条件的元素 j ,从中选出dp[j]的最大值,再加上 1 就是dp[i]的值了。因此,状态转移方程可以表示为:

dp[i] = max(dp[j] + 1)

其中 j < i 且 nums[j] < nums[i]。

3.边界条件:

最长递增子序列问题的边界条件是dp数组的初始状态。通常情况下,初始状态可以是每个元素本身,即dp[i] = 1

4.求解最优值:

最后,c语言求解最长递增子序列的长度:

#include <stdio.h>
// 函数用于计算最长递增子序列的长度
int getLIS(int nums[], int n) { // longestIncreasingSubsequence
    // 定义dp数组,存储以每个元素结尾的最长递增子序列长度
    int dp[n];
    int i, j; 
    // 初始化dp数组,每个元素最短都是一个递增子序列,长度为1
    for (i = 0; i < n; i++) {
        dp[i] = 1;
    }
    // 记录最长递增子序列的长度
    int maxLength = 1;
    // 动态规划过程,根据状态转移方程 
    for (i = 1; i < n; i++) {
        for (j = 0; j < i; j++) {
            if (nums[i] > nums[j] && dp[i] < dp[j] + 1) {
                dp[i] = dp[j] + 1;
                // 更新最长递增子序列的长度
                if (dp[i] > maxLength) {
                    maxLength = dp[i];
                }
            }
        }
    }
    // 返回最长递增子序列的长度
    return maxLength; 
}
int main() {
    int nums[] = {1, 5, 2, 3, 8, 4, 7};
    int n = 7; // 数组长度 
    // 调用函数求最长递增子序列
    int maxLength = getLIS(nums, n);
    printf("最长递增子序列的长度为:%d\n", maxLength);
    return 0;
}
// 输出结果:
// 最长递增子序列的长度为:5

最大子序和问题

最大子序和问题是要求在一个给定的数组中找到一个连续的子数组,使得子数组的和最大。如序列[-2, 1, 4, -2, 3, -5, 1, 1]的最大子数组为[1, 4, -2, 3],最大和为6。

1.状态定义:

为了解决最大子序和问题,我们同样需要定义一个状态数组dp。设dp[i]表示以第i个元素结尾的最大子序和

2.状态转移方程:

对于dp[i],我们需要考虑前面的元素对它的影响,以确定最大子序和。对于第 i 个元素,我们需要比较'不包含前面元素''包含前面元素'两种情况的和哪一个大,选择最大的情况。状态转移方程可以表示为:

dp[i] = max(nums[i], dp[i-1] + nums[i])

3.边界条件:

最大子序和问题的边界条件是dp数组的初始状态。通常情况下,初始状态可以是第一个元素本身,即dp[0] = nums[0]

4.求解最优值:

c语言求解最大子序和问题代码如下:

#include <stdio.h>
// 函数用于计算最大子序和
int maxSubArray(int nums[], int n) {
    // 定义dp数组,存储以每个元素结尾的最大子序和
    int dp[n];
    // 初始化dp数组
    dp[0] = nums[0];
    // 记录最大子序和的值
    int maxSum = nums[0];
    int i; 
    // 动态规划过程
    for (i = 1; i < n; i++) { 
    	if (dp[i - 1] + nums[i] > nums[i]) {
    		dp[i] = dp[i - 1] + nums[i];
		} else {
			dp[i] = nums[i];
		}
        // 更新最大子序和的值
        if (dp[i] > maxSum) {
            maxSum = dp[i];
        }
    }
    // 返回最大子序和的值
    return maxSum; 
}
int main() {
    int nums[] = {-2, 1, 4, -2, 3, -5, 1, 1};
    int n = 8;
    // 调用函数计算最大子序和
    int maxSum = maxSubArray(nums, n);
    printf("最大子序和为:%d\n", maxSum);
    return 0;
}
// 输出结果:
// 最大子序和为:6

写在最后

在这篇文章中,我们学习了一维动态规划问题,并解决了两个经典的问题:最长递增子序列和最大子序和问题。通过定义合适的状态、状态转移方程以及边界条件,我们成功地应用动态规划的思想,高效地求解了这两个问题。

然而一维动态规划问题仅仅是动态规划算法的冰山一角,在下一篇文章中,我们将进一步探讨二维动态规划问题,深入探讨最长公共子序列和最短路径等问题。

敬请期待!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值