最大子序列和、最小子序列和、最小正子序列和、最大子序列乘积

一、先说最大子序列和问题,四种解法,时间复杂度依次递减:

1、O(N^3)

 1 int MaxSubsequenceSum (const int A[], int N)
 2 {
 3     int ThisSum, MaxSum = 0; 
 4     
 5     for(int i = 0; i != N; ++i)
 6         for(int j = i; j != N; ++j){
 7             ThisSum = 0;
 8             for(int k = i; k != j; ++k)
 9                 ThisSum += A[k];
10             if(MaxSum < ThisSum)
11                 MaxSum = ThisSum;
12         }
13     return MaxSum;
14 }

2、O(N^2)

 1 int MaxSubsequenceSum (const int A[], int N)
 2 {
 3     int ThisSum, MaxSum = 0; 
 4     
 5     for(int i = 0; i != N; ++i){ 
 6         ThisSum = 0;
 7         for(int j = i; j != N; ++j){
 8             ThisSum += A[j];
 9             if(MaxSum < ThisSum)
10                 MaxSum = ThisSum;
11         }
12     }
13     return MaxSum;
14 }

3、O(N*logN)--分治递归--假设N是偶数

 1 int Max3(int a, int b, int c)
 2 {
 3     int max;
 4     max = a > b ? a : b;
 5     max = max > c ? max : c;
 6     
 7     return max; 
 8 }
 9 
10 static int MaxSubSum (const int A[], int Left, int Right)//Left左边界(0) Right右边界(N-1) 
11 {
12     int MaxLeftSum, MaxRightSum;
13     int MaxLeftBorderSum, MaxRightBorderSum;
14     int LeftBorderSum, RightBorderSum;
15     int Mid;
16     
17     if(Left == Right)
18         if(A[Left] > 0)
19             return A[Left];
20         else
21             return 0;
22     
23     Mid = (Left + Right) / 2;
24     MaxLeftSum = MaxSubSum(A, Left, Mid);      //左侧递归
25     MaxRightSum = MaxSubSum(A, Mid+1, Right);  //右侧递归 
26     
27     MaxLeftBorderSum = 0; LeftBorderSum = 0;   //相比于第二种算法,去掉一个循环,每次分一半来求解最大值 
28     for(int i = Mid; i != Left-1; --i){
29         LeftBorderSum += A[i];
30         if(LeftBorderSum > MaxLeftBorderSum)
31             MaxLeftBorderSum = LeftBorderSum;
32     }
33     
34     MaxRightBorderSum = 0; RightBorderSum = 0;
35     for(int i = Mid+1; i != Right+1; ++i){
36         RightBorderSum += A[i];
37         if(RightBorderSum > MaxRightBorderSum)
38             MaxRightBorderSum = RightBorderSum;
39     }
40     
41     return Max3(RightBorderSum, MaxRightSum,        // 比较左侧最大值,右侧最大值和左右相加最大值 
42             MaxLeftBorderSum+MaxRightBorderSum);
43 }

4、O(N)

 1 int MaxSubsequenceSum (const int A[], int N)
 2 {
 3     int ThisSum, MaxSum;
 4     ThisSum = MaxSum = 0; 
 5     
 6     for(int i = 0; i != N; ++i){ 
 7         ThisSum += A[i];
 8         if(MaxSum < ThisSum)
 9             MaxSum = ThisSum;
10         else if(ThisSum < 0)
11             ThisSum = 0;
12     }
13     return MaxSum;
14 }

   该算法附带的一个优点是,它只对数据进行一次扫描,一旦A[i]被读入并处理,它就不再需要被记忆。不仅如此,在任何时刻,算法都能对它已经读入的数据给出子序列问题的正确答案。具有这种特性的算法叫联机(online)算法。仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。

二、最小子序列和O(N)

    只是改变判断条件符号的方向。

 1 int MaxSubsequenceSum (const int A[], int N)
 2 {
 3     int ThisSum, MaxSum;
 4     ThisSum = MaxSum = 0; 
 5     
 6     for(int i = 0; i != N; ++i){ 
 7         ThisSum += A[i];
 8         if(MaxSum > ThisSum)
 9             MaxSum = ThisSum;
10         else if(ThisSum > 0)
11             ThisSum = 0;
12     }
13     return MaxSum;
14 }

 三、最小正子序列和

感谢@windrang指出我的错误。

1、O(N^2)

for (int i = 0; i != N; ++i) {
    ThisSum = 0;
    for (int j = i; j != N; ++j) {
        ThisSum += A[j];
        if (MinSum > ThisSum && ThisSum > 0)
            MinSum = ThisSum;
    }
}

2、O(NlogN)

#include <iostream>
#include <algorithm>
#include <string.h>

struct Item {
    int value;
    int index;
};

bool cmp(Item& a, Item& b) 
{ 
    if (a.value == b.value) {
        a.index = b.index = a.index < b.index ? a.index : b.index;
    }
    return a.value < b.value;
}

int MinSubsequenceSum (const int A[], int N)
{
    int ThisSum = 0, MinSum = 0;
    Item* B = new item[N];
    
    memset(B, 0, sizeof(Item) * N);
    
    for (int i = 0; i < N; ++i) {
        B[i].index = i;
        ThisSum += A[i];
        B[i].value = ThisSum;
    }
    std::sort(B, B+N, cmp);
    
    MinSum = B[0].value > 0 ? B[0].value : 2<<29;
    
    for (int i = 1; i < N; ++i) {
        if ((B[i-1].index < B[i].index) && (B[i].value - B[i-1].value > 0) && (B[i].value - B[i-1].value < MinSum)) {
            MinSum =  B[i].value - B[i-1].value;
        }
    }
    delete[] B;
    return MinSum;
}
int main ()
{
    int A[8] = {4,-1,6,-2,-1,3,6,-2}; //  4,  0, 4,  1,  -1
    std::cout << MinSubsequenceSum(A, 8);
    return 0;
}

这里是是根据绿色夹克衫老爷的答案来写的。其中需要注意的是排序,在判断函数中,需要将相同元素值的序列合并为较小的序列。否则的话,序列(4, 0, 4, 1, -1),得出的答案为4。

四、最大子序列乘积

DP

思路:
以元素i结尾序列提供的最大正数记做 pos, 最小负数记做 nag
a[n] 大于零时:
    pos[n] = max{pos[n-1] * a[n], a[n]}
    max_value = max{max_value, pos[n]}
    若n-1位置存在最小负数, 更新 nag[n] = nag[n-1] * a[n]
a[n] 小于零时:
    pos[n] = max{nag[n-1] * a[n], 0.0}
    max_value = max{max_value, pos[n]}
    更新 nag[n] = min{pos[n-1] * a[n], a[n]}
a[n] 等于零时:
    清空 nag[n] 与 pos[n]

代码,

#include <iostream>

inline double max(const double& a, const double& b)
{
return (a > b) ? a : b; } double MaxSubsequenceProduct(double a[], int len) { double Max = 0.0, pos = 0.0, old = 0.0, nag = 1.0; for (int i = 0; i < len; ++i) { if (a[i] > 1e-6) { pos = max(old * a[i], a[i]); Max = max(Max, pos); if (nag < -1e-6) { nag *= a[i]; } } else if (a[i] < -1e-6) { pos = max(0.0, nag * a[i]); Max = max(Max, pos); nag = (old * a[i] > a[i]) ? a[i] : old * a[i]; } else { nag = 1.0; pos = 0.0; } old = pos; } return Max; } int main() { double a[7] = { -2.5, 4, 0, 3, 0.5, 8, -1 }; std::cout << MaxSubsequenceProduct(a, 7); std::cin >> a[2]; return 0; }

出处来自木叶漂舟

 

posted @ 2015-11-07 11:20  clairvoyant  阅读(5739)  评论(5编辑  收藏  举报