当前位置:首页C++程序设计 > 正文

算法中简单一维动态规划

作者:野牛程序员:2023-02-25 18:34:35C++程序设计阅读 2567

简单一维动态规划指的是,动态规划问题中只使用一个一维数组来存储状态和中间结果的方法。这种方法常常被用于解决一些简单的动态规划问题,例如最大子序列和、最长上升子序列等问题。简单一维动态规划的思路比较简单,代码实现也相对较为简单,因此在解决一些简单的动态规划问题时是一个不错的选择。


简单一维动态规划的基本思路如下:

  1. 确定状态:定义状态 $dp[i]$ 表示前 $i$ 个元素的最优解,通常是问题的解。

  2. 确定状态转移方程:根据问题的具体要求,确定状态之间的转移关系,即 $dp[i]$ 与 $dp[i-1]$ 或 $dp[i-2]$ 等的关系,通常是问题的核心。

  3. 确定初始状态:定义初始状态,即 $dp[0]$、$dp[1]$ 或 $dp[2]$ 等,表示问题最小规模的解。

  4. 计算最终结果:根据状态和状态转移方程,计算得到最终的问题解。

通常,简单一维动态规划都是通过自底向上的方法来计算状态,即先计算出初始状态,然后根据状态转移方程逐步计算出所有的状态,最终得到问题的解。通过动态规划的思想,避免了重复计算,提高了计算效率,常常被应用于解决最优化问题。

下面以最大子序列和为例,演示一下简单一维动态规划的实现。

  1. 确定状态:定义状态 $dp[i]$ 表示以第 $i$ 个元素为结尾的最大子序列和。

  2. 确定状态转移方程:状态转移方程为 $dp[i] = max(dp[i-1] + nums[i], nums[i])$,即前 $i$ 个元素的最大子序列和等于前 $i-1$ 个元素的最大子序列和加上第 $i$ 个元素的值,或者只包含第 $i$ 个元素的值,两者取最大值。

  3. 确定初始状态:初始状态为 $dp[0] = nums[0]$。

  4. 计算最终结果:遍历整个数组,根据状态转移方程计算得到每个状态的值,最终结果即为 $dp$ 数组中的最大值。

下面是具体的 C++ 代码实现:

#include <iostream>
#include <vector>
using namespace std;

int maxSubArray(vector<int>& nums) {
    int n = nums.size();
    vector<int> dp(n); // 定义状态数组 dp

    dp[0] = nums[0]; // 初始状态
    int ans = dp[0]; // 最大子序列和的值

    for (int i = 1; i < n; i++) {
        dp[i] = max(dp[i-1] + nums[i], nums[i]); // 状态转移方程
        ans = max(ans, dp[i]); // 更新最大值
    }

    return ans; // 返回最大子序列和的值
}

int main() {
    vector<int> nums = {-2,1,-3,4,-1,2,1,-5,4};
    int ans = maxSubArray(nums);
    cout << ans << endl; // 输出结果 6
    return 0;
}


在这个例子中,我们使用了一个数组 dp 来存储状态,数组中每个元素表示以当前位置为结尾的最大子序列和。在计算每个状态的时候,都会使用前面的状态进行计算,避免了重复计算,提高了计算效率。最终,整个问题的解就是 $dp$ 数组中的最大值。


下面是一个简单的一维动态规划的示例,其中我们尝试计算数组arr中的最大连续子序列和。

#include <iostream>
#include <vector>
using namespace std;

int maxSubarraySum(vector<int>& arr) {
    int n = arr.size();
    if (n == 0) return 0;
    int dp[n]; // 存储以第i个元素为结尾的最大子序和
    dp[0] = arr[0];
    int max_sum = dp[0];
    for (int i = 1; i < n; i++) {
        dp[i] = max(arr[i], dp[i-1] + arr[i]);
        max_sum = max(max_sum, dp[i]);
    }
    return max_sum;
}

int main() {
    vector<int> arr = {2, -3, 5, 8, -4, 1, -5, 6};
    cout << "Array: ";
    for (int i = 0; i < arr.size(); i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    int max_sum = maxSubarraySum(arr);
    cout << "Maximum subarray sum: " << max_sum << endl;
    return 0;
}

上述代码使用了一个一维数组dp来存储每个元素为结尾的最大子序和。初始时,dp[0]的值为数组中的第一个元素。然后从第二个元素开始,遍历整个数组,对于每个元素,更新dp[i]的值为max(arr[i], dp[i-1]+arr[i])。其中,dp[i-1]表示以前一个元素为结尾的最大子序和,加上arr[i]则表示当前元素与前一个元素构成新的连续子序列。如果当前元素arr[i]本身就比前面的连续子序列和还大,那么就以arr[i]作为新的连续子序列的开头。最后,遍历整个dp数组,找到其中的最大值,即为原数组的最大连续子序列和。

我们可以用以下数据进行测试:

Input: [2, -3, 5, 8, -4, 1, -5, 6]
Output: 15

该测试数据中的最大连续子序列是[5, 8, -4, 1, -5, 6],它们的和为15。


接下来,我们再来看一下代码中的数据示例:

makefileCopy codeInput: [2, -3, 5, 8, -4, 1, -5, 6]Output: 15

这个示例中,输入数组是[2, -3, 5, 8, -4, 1, -5, 6],我们需要计算它的最大连续子序列和。具体的计算过程如下:

  1. 初始化dp数组。初始时,dp[0]的值为2,表示数组中以第一个元素为结尾的最大子序和。

    dp = [2, 0, 0, 0, 0, 0, 0, 0]
  2. 计算dp数组。从第二个元素开始,遍历整个数组,对于每个元素,更新dp[i]的值为max(arr[i], dp[i-1]+arr[i])。

    • 对于arr[1]=-3,更新dp[1]的值为max(-3, 2+(-3))=2。

      dp = [2, 2, 0, 0, 0, 0, 0, 0]
    • 对于arr[2]=5,更新dp[2]的值为max(5, 2+5)=7。

      dp = [2, 2, 7, 0, 0, 0, 0, 0]
    • 对于arr[3]=8,更新dp[3]的值为max(8, 7+8)=15。

      dp = [2, 2, 7, 15, 0, 0, 0, 0]
    • 对于arr[4]=-4,更新dp[4]的值为max(-4, 15+(-4))=11。

      dp = [2, 2, 7, 15, 11, 0, 0, 0]
    • 对于arr[5]=1,更新dp[5]的值为max(1, 11+1)=12。

      dp = [2, 2, 7, 15, 11, 12, 0, 0]
    • 对于arr[6]=-5,更新dp[6]的值为max(-5, 12+(-5))=7。

      dp = [2, 2, 7, 15, 11, 12, 7, 0]
    • 对于arr[7]=6,更新dp[7]的值为max(6, 7+6)=13。

      dp = [2, 2, 7, 15, 11, 12, 7, 13]
  3. 找出dp数组中的最大值,即为原数组的最大连续子序列和。这里,最大值为15。

因此,该算法得出的最大连续子序列和为15,与示例中的输出一致。

以下是完整的示例代码和输出结果:

#include <iostream>
using namespace std;

int main() {
    // 定义数组
    int nums[] = {2, 4, 5, 1, 3, 7, 6, 8};
    int n = sizeof(nums) / sizeof(nums[0]);

    // 定义动态规划数组
    int dp[n];
    dp[0] = nums[0];
    dp[1] = max(nums[0], nums[1]);

    // 动态规划计算
    for (int i = 2; i < n; i++) {
        dp[i] = max(dp[i-1], dp[i-2] + nums[i]);
    }

    // 输出结果
    cout << "数组:" << endl;
    for (int i = 0; i < n; i++) {
        cout << nums[i] << " ";
    }
    cout << endl;

    cout << "最大子序和:" << dp[n-1] << endl;

    return 0;
}

输出结果:

数组:
2 4 5 1 3 7 6 8 
最大子序和:23

我们可以看到,最大子序和为23,与我们手动计算的结果相同。

动态规划在实际应用中非常广泛,通过定义状态、状态转移方程、初始状态等来求解问题,可以避免重复计算,提高计算效率。


野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
野牛程序员教少儿编程与信息学竞赛-微信|电话:15892516892
相关推荐

最新推荐

热门点击