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

根据前面文章所提到的最大子序列和https://blog.csdn.net/weixin_41223818/article/details/80573351
接下来对其拓展,说说最小子序列和、最小正子序列和、最大子序列乘积的解法

最小子序列和

这个问题可以直接模仿最大子序列和的联机算法,改一下判断条件即可

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

最小正子序列和

即连续子序列和要大于零,且要最小
这个问题感觉模仿上面的联机算法已经行不通了,因为没法确定何时将ThisSum重置为0,所以只能模仿最大子序列和的O(N^2)算法与O(NlogN)的算法.
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)
这里的思路与最大子序列和有所不同
假设序列为 4,-1,5,-2,-1,2,6,-2
先求一下从第一位开始的到第i位的累加,最前面加上0(一个都不选的情况)
4,-1,5,-2,-1,2,6,-2 => 0 4 3 8 6 5 7 13 11

对这个累加的数列排个序,0 3 4 5 6 7 8 11 13,并记录对应的下标0 2 1 5 4 6 3 8 7,然后只要判断邻近的两个数是否可以组成序列,比如4和3就不可以,因为4 > 3而4对应下标为1,3对应为2。4和5就可以,4对应下标1,5对应下标5,这样的话,从第1个数到第4个数,就组成了一个和为1的序列。

解释一下为什么只需检查相邻2个数就可以,设ABC是排序后的结果,如果A同B不能组成序列,而A同C可以组成序列,那么B同C也可以组成序列,并且BC会是一个更优的解。

排序的时候处理一下,数字相等的话可以合并成1个元素,只记录索引的最大最小值就可以。

#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;
}

最大子序列乘积

思路:
以元素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;
}

参考资料

http://www.cnblogs.com/clairvoyant/p/4944737.html

猜你喜欢

转载自blog.csdn.net/weixin_41223818/article/details/80688583