【动态规划】使用动态规划法求最大连续子序列和

通过动态规划方法求最大连续子序列和

问题描述:

给定一个有n(n >= 1)个整数的序列,求出其中最大连续子序列的和。如:{-2, 11, -4, 13, -5, -2},最大的连续子序列是:{11, -4, 13}和为20

【规定】一个序列的最大连续子序列和至少是0,如果小于0,其结果为0。

解法:

使用一个整型数组arr[]来存储序列,即int arr[] = {0, -2, 11, -4, 13, -5, -2},为了处理方便,其中0号下标不用,从1开始处理。

使用一个动态规划数组dp[i]存储以第i个为结束位置的子序列和。并给初值:dp[0] = 0dp[i]dp[i - 1] 的关系为:

dp[i] < 0,按照约定,赋值为0,若dp[i] > 0 则继续累加,即 dp[i] = dp[i - 1] + arr[i]

dp[i] = dp[i - 1] + arr[i];
if (dp[i] < 0)
{
    dp[i] = 0;
}

按照上述,推导序列 {-2, 11, -4, 13, -5, -2}

dp[0] = 0, dp[1] = dp[0] + arr[1] == -2, 因为dp[1] < 0, 则dp[1] = 0

dp[1] = 0, dp[2] = dp[1] + arr[2] == 11, 因为 dp[2] > 0, 则不做处理

dp[2] = 11, dp[3] = dp[2] + arr[3] == 7, 因为dp[3] > 0, 不做处理

dp[3] = 7, dp[4] = dp[3] + arr[4] == 20, 因为dp[4] > 0, 不做处理

dp[4] = 20, dp[5] = dp[4] + arr[5] == 15

dp[5] = 15, dp[6] = dp[5] + arr[6] == 13

dp[6] = 13, 到达数组结尾, 结束遍历.

既得动态规划数组 dp[i]{0, 0, 11, 7, 20, 15, 13}

这一部分的代码实现如下:

dp[0] = 0;
for (int i = 0; i < SIZE; i++)
{
    //求dp[]数组第i个元素的值
    dp[i] = dp[i - 1] + arr[i];
    //如果dp[i]的值小于0,则置0,从第i+1个再重新选
    if (dp[i] <= 0)
    {
        dp[i] = 0;
    }
}

得到了dp[]数组, 那么只要通过遍历它就可以得到最大子序列的和.

//找到最大值的下标
int end = 0;
for (int i = 0; i < SIZE; i++)
{
    if (dp[end] < dp[i])
    {
        end = i;
    }
}

其中end为最大连续子序列的结束位置, 在本例中为4

现在, 还剩下最后一个问题: 怎么推导出最大连续子序列是由哪几个元素组成的?

其实只需要从end 向前遍历到第一个dp[i] == 0的位置就可以确定该子序列是从i + 1的位置开始的.

因为dp[i] != 0dp[i] - dp[i - 1] == arr[i], 则:

dp[4] - dp[3] = a[4] = 13
dp[3] - dp[2] = a[3] = -4
dp[2] - dp[1] = a[2] = 11
dp[1] - dp[0] = a[1] = 0 == dp[1]

因为dp[i] < 0 的时候, 会把dp[i]置为0, 而对于dp[i] > 0 时不会做这个操作, 所以从后向前遍历, 找到第一个为0的元素 i, 则该子序列的起始位置就在下一位i + 1.

int start = 0;
for (int i = end; i >= 0; i--)
{
    //第一个dp[i]==0的下一位是起始位置
    if (dp[i] == 0)
    {
        start = i + 1;
        break;
    }
}

接下来就是打印结果了:

//打印dp数组
cout << "打印dp数组:"  << endl;
for (int i = 0; i < SIZE; i++)
{
    cout << dp[i] << " " ;
}
//打印最大连续子序列:
cout << "打印最大连续子序列" << endl;
for (int i = start; i <= end; i++)
{
    cout << arr[i] << endl;
}

综上所述:

#include <iostream>

using namespace std;

//描述问题规模(数组长度)
#define SIZE sizeof (arr) / sizeof(arr[0])

//问题描述(要求子序列的数组)
int arr[] = {0, -2, 11, -4, 13, -5, -2};
//动态规划数组
int dp[sizeof (arr) / sizeof (arr[0])];

void solve()
{
    //初始化dp[0]
    dp[0] = 0;
    for (int i = 0; i < SIZE; i++)
    {
        //求dp[]数组第i个元素的值
        dp[i] = dp[i - 1] + arr[i];
        //如果dp[i]的值小于0,则放弃,从第i+1个再重新选
        if (dp[i] <= 0)
        {
            dp[i] = 0;
        }
    }
    
    //找到最大值的下标
    int end = 0;
    for (int i = 0; i < SIZE; i++)
    {
        if (dp[end] < dp[i])
        {
            end = i;
        }
    }

    //溯源,找到起始位置
    int start = 0;
    for (int i = end; i >= 0; i--)
    {
        //第一个dp[i]==0的下一位是起始位置
        if (dp[i] == 0)
        {
            start = i + 1;
            break;
        }
    }
    
    //打印dp数组
    cout << "打印dp数组:"  << endl;
    for (int i = 0; i < SIZE; i++)
    {
        cout << dp[i] << " " ;
    }
    cout << endl;
    
    //打印最大连续子序列:
    cout << "打印最大连续子序列: " << endl;
    for (int i = start; i <= end; i++)
    {
        cout << arr[i] << endl;
    }
}

int main(void)
{
    solve();
    
    return 0;
}

输出结果:

打印dp数组:
0 0 11 7 20 15 13
打印最大连续子序列:
11
-4
13
posted @ 2024-06-01 18:54  codels  阅读(250)  评论(0)    收藏  举报