给定一个长度为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
带记忆的递推法(比直接穷举快了不少)(o(n^2)):
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;
}
}
}