通过动态规划方法求最大连续子序列和
问题描述:
给定一个有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] = 0
。dp[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] != 0
时 dp[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
标签:11,arr,规划法,13,数组,序列,动态,dp
From: https://www.cnblogs.com/codels/p/18226267