Max Sum(最大子段和问题)

本文探讨了最大子段和问题,给出了如何找出序列中元素和最大的子序列的方法。通过动态规划(dp)策略,公式表示为:dp(i) = max(dp(i-1) + ans(i), ans(i))。文章以HDU1003和POJ2479两道题目为例,详细解释了解决这类问题的步骤。在HDU1003中,通过维护变量sum和记录起点head来更新最大子段和。而在POJ2479中,需要找到两个互不相交子序列的和的最大值,可以通过两次遍历分别计算正序和逆序的dp值来解决。" 131027869,10385585,Python升序排序:Lambda函数的秘密,"['Python', '算法', '编程技巧']

最大子段和:给定一个序列(元素可正可负),找出其子序列中元素和最大的值。

我们用dp(i)表示序列中以元素ans(i)结尾的序列的最大子段和,那么有:dp(i)=max(dp(i-1)+ans(i),ans(i));

#define INF 999999999
int solve(int ans[],int n)  
{  
    dp[0]=0;  
    int nmax=-INF;  
    for(int i=1;i<=n;i++)  
    {  
        dp[i]=max(dp[i-1]+ans[i],ans[i]);  
        nmax=max(nmax,dp[i]);  
    }  
    return nmax;  
}  


首先来看一道比较基础的最大子段和问题--HDU1003   Max Sum:

题目大意:给定一个序列,让求出此序列的最大子段和,并输出该子序列在原序列中的位置。

这里我们用变量sum来代替dp()数组,如果sum+a(i)<a(i),就更新sum的值,并重新纪录head的值,如果sum+a(i)>=a(i),则只需更新sum的值;

代码如下:

#include <cstdio>
#include <iostream>
using namespace std;
const int MAX=100005;
int main()
{
    int t,n,a[MAX],i;
    int T=0;
    while(scanf("%d",&t)!=-1)
      while(t--)
      {
          T++;
          scanf("%d",&n);
          int sum,max,head,tail,x;
          for(i=1;i<=n;i++)
            scanf("%d",&a[i]);
          head=tail=x=1;
          max=sum=a[1];
          for(i=2;i<=n;i++)
          {
              if(sum+a[i]<a[i])
              {
                  x=i;
                  sum=a[i];
              }
              else
              {
                  sum+=a[i];
              }
              if(sum>max)
              {
                  max=sum;
                  tail=i;
                  head=x;
              }

          }
          printf("Case %d:\n%d %d %d\n",T,max,head,tail);
          if(t) printf("\n");
      }
      return 0;
}


 

下面看POJ上一道最大子段和的问题    http://poj.org/problem?id=2479

题目大意:给你一个序列,求出这个序列中两个互不相交的子序列的和的最大值。

由于找两个互不相交的子序列,我们可以从左到右,再从右到左分别求出这个序列的dp()值,然后在这些dp()值互不重叠的情况下分别找出两个dp()的最大值相加即可。具体实现呢,可以简化这个思想,只需找出一个dp()值就可以,我们找出正序的dp()数组,对于逆序的情况呢,用一层for循环,每一次循环,我们用一个变量temp来存放从i到n的这些子序列中的最大子段和,然后和1到i的最大子段和相加,这样经历n次循环之后我们就找到了所要求的值。

代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#define INF -999999999
using namespace std;
int ans[50050],dp[50050];
int main()
{
    int t,n,i;
    cin>>t;
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%d",&n);
        for(i=1;i<=n;i++)
          scanf("%d",&ans[i]);
        for(i=1;i<=n;i++)
          dp[i]=max(dp[i-1]+ans[i],ans[i]);
        int temp=0;
        int solve=INF,nmax=INF;
        for(i=n;i>1;i--)
        {
            temp=max(temp+ans[i],ans[i]);
            nmax=max(nmax,temp);
            if(solve<nmax+dp[i-1])
              solve=nmax+dp[i-1];
        }
        printf("%d\n",solve);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值