最大子段和问题(dp大法)

给定一个长度为n的一维数组a,请找出此数组的一个子数组,使得此子数组的和sum=a[i]+a[i+1]+……+a[j]最大,其中i>=0,i<n,j>=i,j<n,例如
   31 -41 59 26 -53  58 97 -93 -23 84

 子矩阵59+26-53+58+97=187为所求的最大子数组。

题目很短,如果没有时间限制的话,穷举大法确实简单,但是有时间限制,只能用dp大法啦

思想:

  • 令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
  • 子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
  • 如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
  • 如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大
接下来代码就简单了:

#include<stdio.h>
#include<cmath>
#include<iostream>
#include<map>
#include<string.h>
#define maxn 500
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int map[maxn][maxn];
bool vis[maxn][maxn];
int b[maxn];
int a[maxn];
int ans;
int main()
{
    int n;
    while(cin>>n)
    {
        mem(b,0);
        mem(a,0);
        for(int i=1; i<=n; i++)
        {
            cin>>a[i];
        }
        int sum=0;
        for(int i=1; i<=n; i++)
        {//核心代码:
            if(b[i-1]>0)
            {
                b[i]=b[i-1]+a[i];
            }
            else
            {
                b[i]=a[i];
            }
        }
        for(int i=1; i<=n; i++)
        {
            if(b[i]>sum)
                sum=b[i];
        }
        printf("%d\n",sum);
    }
    return 0;
}

穷举大法就写个大佬的代码吧:

直接穷举法o(n^3):

int start = 0;
int end = 0; 
int max = 0;
for(int i = 1; i <= n; ++i)
{
for(int j = i; j <= n;++j)
{
int sum = 0;
for(int k = i; k <=j; ++k)
sum += a[k];
if(sum > max)
{
start = i;
end = j;
max = sum;
}
}
}

带记忆的递推法(比直接穷举快了不少)(o(n^2)):
int start = 0;
int end = 0;
int max = 0;
for(int i = 1; i <= n; ++i)
{
int sum = 0;
for(int j = i; j <= n;++j)
{
sum += a[j];
if(sum > max)
{
start = i;
end = j;
max = sum;
}
}
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章