校招季——编程题目(8-9)

2013/08/18,后天就回本部准备校招了,争取今天能将题目总结到70题。

8.      最大子序列和

题目:

输入一个长度为n的序列,其中元素有正有负,求其中序列和最大的连续子序列,如果不存在序列和为正数的连续子序列,返回0

解答:

方法一:

思路:遍历所有子序列,求出序列和,找出其中和最大的那个。时间复杂度O(n3),空间复杂度O(1)

方法二:

思路:先求出长度为n的辅助数组sum,其中sum[i]data[0,i]的和,再遍历所有子序列,子序列[i, j]的和可以用sum[j]-sum[i-1]一步求出,找出其中和最大的那个,时间复杂度O(n2),空间复杂度O(n),改进了方法一中求序列和的部分。

方法三:

思路:将数组分成前后两部分,则这个最大和的子序列要么在前面,要么在后面,要么横跨中间,因此适用于分治法。时间复杂度O(nlogn),空间复杂度O(1)

int MaxSum(const int *src, int n)
{
    if (n == 1)
        return src[0] < 0? 0: src[0]; 
    int m = n/2, i; 
    int sum1 = 0,sum2 = 0,sum = 0; 
    for (i = m - 1; i >= 0; --i)
    {
        sum += src[i]; 
        if (sum > sum1)
            sum1 = sum; 
    }
    sum = 0; 
    for (i = m; i < n; ++i)
    {
        sum += src[i]; 
        if (sum > sum2)
            sum2 = sum; 
    }
    return Max3(MaxSum3(src, m), MaxSum3(src + m, n - m), sum1 + sum2); 
}

方法四:

思路:遍历数组累加时,如果当前得到的和小于0,则表明前面这段肯定不在最大和的子序列中,因此可以丢弃这一段,重新开始累加就可以了。

用状态方程来表示,我们令F(n)表示[0,n]区间内以d[n]为结尾的最大和,d为原序列,那么有:

F(n+1) = Max{0,F(n),F(n)+d[n+1]}

代码:

int MaxSum(const int *src, int n)
{
    int max_sum = 0,sum = 0,i;
    for (i = 0; i < n; ++i)
    {
        sum += src[i];
        if (sum < 0)
            sum = 0;
        if (sum > max_sum)
            max_sum = sum;
    }
    return max_sum;
}

9.      最大子序列积

题目:

输入一个长度为n的序列,其中元素有正有负,求其中序列和最大的连续子序列,如果不存在序列和为正数的连续子序列,返回0

解答:

思路:由于负数的存在,我们不能简简单单只存一个当前最大值,我们还需要存当前最小值。我们令F(n)表示[0,n]区间内以d[n]为结尾的最大积,令G(n)表示[0,n]区间内以d[n]为结尾的最小积,d为原序列,那么有:

F(n + 1) = Max{F(n) * d[n + 1], d[n + 1], G(n) * d[n + 1]}

G(n + 1) = Min{F(n) * d[n + 1], d[n + 1], G(n) * d[n + 1]}

代码:

int MaxProduct(const int *src,int n)
{
    int max = 1, min = 1, max_product = 0, i;
    for (i = 0; i < n; ++i)
    {
        int newmax = max * src[i];
        int newmin = min * src[i];
        max = Max3(src[i],newmax,newmin);
        min = Min3(src[i],newmax,newmin);
        if(max > max_product)
            max_product = max;
    }
    return max_product;
}

点赞