最大子序列和问题(动态规划)

最大子序列和问题(动态规划)
1.问题详解

    输入一组整数,求出这组数字子序列和中最大值。也就是只要求出最大子序列的和。例如:

    序列:-2 11 -4 13 -5 -2,则最大子序列和为20。

    序列:-6 2 4 -7 5 3 2 -1 6 -9 10 -2,则最大子序列和为16。

2.解决办法

    这一类题目我们通常采用动态规划来解决,即可以通过一次遍历完成对于最大子序列以及相应位置坐标的求解。

    思路如下(假设存在正数):

         (1)我们需要理解,如果一个数是负数,那么它不可能是起点,任何负数序列不可能为最大子序列和的序列。

         (2)我们从第一个元素往后遍历,所记录元素的数组为a[],对于这些输入的数组元素,我们设一个和thisSum,初始化为0。对应这个thisSum,我们设置一个记录最大数值

                   和的元素maxSum,每次输入一个数组元素,如果thisSum比maxSum大,则进行更新。

         (3)如果thisSum<0,那么前面这一段的最大值已经记录完毕,存在maxSum里面,造成thisSum<0的原因是该元素<0,因此,在后面的元素的遍历中,不能继续使用该
                   元素,因此,将thisSum置为0,继续从该元素的下一个元素为起点进行遍历。

  注意:很多同学可能不能理解,为什么如果是中间一块儿最大,还要从头开始遍历呢。我们可以想想,如果中间最大,那么左右两边肯定不管扩大,两边的和都是负数。因此,当从头到尾遍历时,若thisSum<0了,那么则不继续计入计算。

3.代码(不要求记录位置)

int max(const vector<int>& a) 
 
{ 
 
       int maxSum = 0, thisSum = 0; 
 
       for (int j = 0; j < a.size(); j++) 
 
       { 
 
              thisSum += a[j]; 
 
              if (thisSum > maxSum) 
 
                     maxSum = thisSum; 
 
              else if (thisSum < 0) 
 
                     thisSum = 0; 
 
       } 
 
       return maxSum; 
 
}

4.代码(要求输出位置)

#include <iostream>
#include <cstdio>
using namespace std;
int a[10005];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int maxSum=0,thisSum=0,s=0,e=0,star=0,en=n-1;//初始化,起点终点游动坐标为s,e;记录坐标为star、en
        for(int i=0; i<n; i++)
        {
            scanf("%d",&a[i]);
            if(thisSum>=0)//终点右移,thisSum更新
            {
                e=i;
                thisSum+=a[i];
            }
            else//thisSum<0(这里的thisSum没有加上a[i]),更新此时的起点终点到下一个坐标位
            {
                s=i;
                e=i;
                thisSum=a[i];
            }
            if(thisSum>maxSum|| (thisSum==0&&en==n-1))//当取得thisSum的值大于maxSum时,进行更新,记录坐标
            {
                star=s;
                en=e;
                maxSum=thisSum;
            }
        }
        printf("%d %d %d\n",maxSum,a[star],a[en]);
    }
    return 0;
}

题目:
给定K个整数组成的序列{ N​1​​, N​2​​, …, N​K​​ },“连续子列”被定义为{ N​i​​, N​i+1​​, …, N​j​​ },其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 11, -4, 13, -5, -2 },其连续子列{ 11, -4, 13 }有最大的和20。现要求你编写程序,计算给定整数序列的最大子列和。

记得这题做了两次,都不会动态规划 大哭,今天要写个总结,加深印象。
设dp[n]为以a[n]结尾的最大子列和
有递推关系:dp[n]=max(0,dp[n-1])+a[n]。
有些人可能会想,子列不应该要记录起点和终点吗?其实,在这里可以不需要,比如-2 4 -3 8,那dp[2]=4, dp[2]=1, dp[3]=9,9为最大值,是从4一直加到8,也就是说在计算dp的时候已经隐性的考虑了起点(自己的理解 大笑)
最后,最大子列和即为dp[n]中的最大值。
上代码:



```cpp
```cpp
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int a[100010];
int dp[100010];
int max(int x,int y){return x>y?x:y;}
int main()
{
 int k;
 int i;
 scanf("%d",&k);
 for(i=0;i<k;i++)
 {
  scanf("%d",&a[i]);
 }
 memset(dp,0,sizeof(dp));//初始化dp
 dp[0]=a[0];
 for(i=1;i<k;i++)
 {
  dp[i]=max(0,dp[i-1])+a[i];
 }
 int ans=dp[0];
 for(i=0;i<k;i++)
 {
  if(ans<dp[i]){ans=dp[i];}
 }
 printf("%d\n",ans);
 return 0;
}
添加 printf(“%d\n”,a[i]);
用于记录首尾位置数字

for(i=0;i<k;i++)
 {
  if(ans<dp[i]){ans=dp[i];
  printf("%d\n",a[i]);}
  
 }

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值