以最大连续和为例的算法分析

来源:互联网 发布:高新区行知小学好不好 编辑:程序博客网 时间:2024/05/17 08:39

学习刘汝佳老师的《算法竞赛》总结

最大连续和问题
给出一个长度为n的序列A1,A2,……,An,要求找到1ijn,使得Ai+Ai+1+……+Aj尽量大。
一.使用枚举,程序如下:

tot = 0;    best = A[1]; //初始最大值    for (int i = 1; i <= n; i++)        for (int j = i; j <= n; j++) {  //检查连续子序列A[i],……,A[j]            int sum = 0;            for (int k = i; k <= j; k++) {                sum += A[k];       //累加元素和                tot++;            }            if (sum > best)                best = sum;       //更新最大值        }

设输入规模为n时,加法操作的次数为T(n),则

T(n)=i=1nj=inji+1=i=1n(ni+1)(ni+2)2=n(n+1)(n+2)6

用一个记号来表示:T(n)=O(n3)

.下面试着优化一下这个算法。
设Si=A1+A2+……+Ai,则Ai+Ai+1+……+Aj=Sj-Si1,其直观含义是”连续子序列之和等于两个前缀和之差“。这样可以省略最内层的循环。

S[0] = 0;    for (int i = 1; i <= n; i++)        S[i] = S[i - 1] + A[i];         //递推前缀和S    for (int i = 1; i <= n; i++)        for (int j = i; j <= n; j++)            best = max(best, S[j] - S[i - 1]);  //更新最大值

类似的方法可以分析出:

T(n)=i=1nni+1=n(n+1)2

时间复杂度是O(n2).

三.分治法
分治算法一般分为以下三个步骤:
划分问题:把问题的实例划分为子问题;
递归求解:递归解决子问题;
合并问题:合并子问题的解得到原问题的解。
在本例中,”划分“就是把序列尽量分成元素个数相等的两半;”递归求解“就是分别求出完全位于左半和完全位于右半的最佳序列;”合并“就是求出起点位于左半,终点位于右半的最大连续和序列,并和子问题的最优解比较。

int maxsum(int *A, int x, int y) {     //返回数组在左闭右开区间[x,y)中的最大连续和    int V, L, R, maxs;    if (y - x == 1)        return A[x];    int m = x + (y - x) / 2;     //分治第一步:划分成[x,m)和[m,y)    maxs = max(maxsum(A, x, m), maxsum(A, m, y));    //分治第二步:递归求解    V = 0;    L = A[m - 1];                 //分治第三步:合并(1)——从分界点开始往左的最大连续和L    for (int i = m - 1; i >= x; i--)        L = max(L, V += A[i]);    V = 0;    R = A[m];                   //分治第三步:合并(2)——从分界点开始往右的最大连续和R    for (int i = m; i <= y; i++)        R = max(R, V += A[i]);    return max(maxs, L + R);         //把子问题的解与L + R比较}

递归方程T(n)=2T(n/2)+O(n),T(1)=1,解为T(n)=O(n log n).

四.算法分析结果
表-运算量随着规模的变化

运算量 最大规模 速度扩大两倍后 n! 11 11 2n 26 27 n3 464 584 n2 10000 14142 nlog2n 4.5x106 8.6x106 n 100000000 200000000

借鉴此表,在算法竞赛中,一个指明n8的题目可能n!的算法已经足够,n20的题目需要用到2n 的算法,而n300的题目可能必须用至少n3的多项式时间算法了。

阅读全文
0 0
原创粉丝点击