给定一个整数序列,序列中可以有负数,求这个序列的最大子段和。
分析:设dp[i]表示以a[i]为结尾的最大子段和,那么很容易写出状态转移方程:
代码:
int Work(int a[],int n)
{
dp[0] = 0;
int ans = -INF;
for(int i=1;i<=n;i++)
{
dp[i] = max(dp[i-1] + a[i],a[i]);
ans = max(ans,dp[i]);
}
return ans;
}
如果要求输出最大子段和在原序列中的起始位置,那么我们用一个数组 f[]来记录哪些元素被选入,f[i] = true表示dp[i]
的计算结果中使用了dp[i-1],这样我们可以知道,只要这样递推下去,那么最大子段和的那部分所有f[i] = true,我们只
需要记录最大子段和的最后一个元素的位置,然后往前推,到第一个为f[i] = false为止。
int Work(int a[],int n)
{
L = R = 0;
dp[0] = a[0];
f[0] = false;
int ans = a[0];
for(int i=1; i<n; i++)
{
if(dp[i-1] < 0)
{
f[i] = false;
dp[i] = a[i];
}
else
{
f[i] = true;
dp[i] = dp[i-1] + a[i];
}
if(ans < dp[i])
{
ans = dp[i];
R = i;
}
}
for(int i=R; i>=0; i--)
{
if(!f[i])
{
L = i;
break;
}
}
L++; R++;
return ans;
}
题目:http://poj.org/problem?id=2479
题意:求一个数列中两个不重叠子序列的最大和。
分析:根据求最大子段和,然后就是用到这道题的精髓了:正序和反序各求一次上面的dp,然后枚举中间的点,把序列分成两
边,在两边各自找出其sum最大的子段,两个值之和最大就是答案。
#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;
const int N = 50005;
const int INF = 1 << 30;
int a[N],dp[N],L[N],R[N];
int Work(int a[],int n)
{
dp[0] = dp[n+1] = -INF;
L[0] = R[n+1] = -INF;
for(int i=1; i<=n; i++)
dp[i] = max(dp[i-1] + a[i],a[i]);
for(int i=1; i<=n; i++)
L[i] = max(dp[i],L[i-1]);
for(int i=n; i>=1; i--)
dp[i] = max(dp[i+1] + a[i],a[i]);
for(int i=n; i>=1; i--)
R[i] = max(dp[i],R[i+1]);
int ans = -INF;
for(int i=1; i<n; i++)
ans = max(ans,L[i] + R[i+1]);
return ans;
}
int main()
{
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
printf("%d\n",Work(a,n));
}
return 0;
}
题目:http://acm.hdu.edu.cn/showproblem.php?pid=1024
题意:给一个序列a[],求其分成m段不相交的子段和的最大值。
分析:用dp[i][j]表示有前j个数组成i组的和的最大值。那么有:
注意max(dp[i-1][k])就是上一组0....j-1的最大值。我们可以在每次计算dp[i][j]的时候记录下前j个的最大值,用数组
保存下来,下次计算的时候可以用,这样时间复杂度为n^2.
int Work(int a[],int n,int m)
{
int ans = -INF;
memset(dp,0,sizeof(dp));
memset(tmp,0,sizeof(tmp));
for(int i=1;i<=m;i++)
{
ans = -INF;
for(int j=i;j<=n;j++)
{
dp[j] = max(dp[j-1] + a[j],tmp[j-1] + a[j]);
tmp[j-1] = ans;
ans = max(ans,dp[j]);
}
}
return ans;
}
最大子矩阵:其实就是把最大子段和推广到二维即可。
int MaxSum(int a[],int n)
{
int ans = a[0];
int tmp = a[0];
for(int i=1; i<n; i++)
{
tmp = max(tmp + a[i],a[i]);
if(tmp > ans) ans = tmp;
}
return ans;
}
int Work(int a[][N],int n,int m)
{
int ans = a[0][0];
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
sum[j] = 0;
for(int j=i;j<n;j++)
{
for(int k=0;k<m;k++)
sum[k] += a[k][j];
int tmp = MaxSum(sum,n);
if(tmp > ans) ans = tmp;
}
}
return ans;
}