转载!写的很好:经典算法问题 - 最大连续子数列和 - 代码文章
动态规划
很多动态规划算法非常像数学中的递推。我们如果能找到一个合适的递推公式,就能很容易的解决问题。
我们用dp[n]表示以第n个数结尾的最大连续子序列的和,于是存在以下递推公式:
dp[n] = max(0, dp[n-1]) + num[n]
仔细思考后不难发现这个递推公式是正确的,则整个问题的答案是max(dp[m]) | m∈[1, N]
。C语言代码如下:
#include <stdio.h>
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N, num[134217728];
int main()
{
//输入数据
scanf("%d", &N);
for(int i = 1; i <= N; i++)
scanf("%d", &num[i]);
num[0] = 0;
int ans = num[1];
for(int i = 1; i <= N; i++) {
if(num[i - 1] > 0) num[i] += num[i - 1];
else num[i] += 0;
if(num[i] > ans) ans = num[i];
}
printf("%d\n", ans);
return 0;
}
已知一个sum数组,sum[i]
表示第1个数到第i个数的和,于是sum[j] - sum[i-1]
表示第i个数到第j个数的和。
那么,以第n个数为结尾的最大子序列和有什么特点?假设这个子序列的起点是m,于是结果为sum[n] - sum[m-1]
。并且,sum[m]
必然是sum[1],sum[2]...sum[n-1]
中的最小值!这样,我们如果在维护计算sum数组的时候,同时维护之前的最小值, 那么答案也就出来了!为了节省内存,我们还是只用一个num数组。C语言代码如下:
#include <stdio.h>
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N, num[134217728];
int main()
{
//输入数据
scanf("%d", &N);
for(int i = 1; i <= N; i++)
scanf("%d", &num[i]);
//计算数组前缀和,并在此过程中得到答案
num[0] = 0;
int ans = num[1], lmin = 0;
for(int i = 1; i <= N; i++) {
num[i] += num[i - 1];
if(num[i] - lmin > ans)
ans = num[i] - lmin;
if(num[i] < lmin)
lmin = num[i];
}
printf("%d\n", ans);
return 0;
}
总结:
#include <stdio.h>
int main()
{
int N, n, s, ans, m = 0;
scanf("%d%d", &N, &n); //读取数组长度和序列中的第一个数
ans = s = n; //把ans初始化为序列中的的第一个数
for(int i = 1; i < N; i++) {
if(s < m) m = s;
scanf("%d", &n);
s += n;
if(s - m > ans)
ans = s - m;
}
printf("%d\n", ans);
return 0;
}