最大子段和問題(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;
}
}
}

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