最大子序列问题

最大子数组问题

定义

给定整数A1, A2, …, An(其中可能是负数),求k的最大值和序列的起始位置(为了方便起见,如果所有整数均为负数,则最大子序列和为0),使用四种算法(根据运行时间区分)解决这个问题。

运行时间为θ(n3)

使用了三个for循环,在最坏情况下,运行时间为θ(n3)

C语言实现代码

#include<stdio.h>
#define LEN(array) (sizeof(array)/sizeof(array[0])) 

void maxsubarray(int *array, int len, int *, int *);

main(){
    int array[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};
    int i, start, end;

    printf("原始数组:\n");
    for (i = 0; i < LEN(array); i++)
        printf("%5d", array[i]);

    putchar(‘\n‘);
    maxsubarray(array, LEN(array), &start, &end);
    printf("最大子序列的起始位置为:%d, 终止位置为:%d\n", start+1, end+1);
    printf("最大子序列为: ");    

    for (i = start; i <= end; i++)
        printf("%5d", array[i]);
    putchar(‘\n‘);
    putchar(‘\n‘);
}

void maxsubarray(int *array, int len, int *start, int *end){
    int i, j, k, sum, maxsum;

    maxsum = 0;
    for (i = 0; i < len; i++){
        for (j = i; j < len; j++){
            sum = 0;
            for (k = i; k <= j; k++){
                sum += array[k];
                if (sum > maxsum){
                    maxsum = sum;
                    *start = i;
                    *end = j;
                }
            }
        }
    }

    printf("\n子序列的求和最大值为: %d\n", maxsum);
}

运行时间为θ(n2)

使用了两个for循环,最坏情况下运行时间为θ(n2)

C语言实现代码

#include<stdio.h>

#define LEN(array) (sizeof(array)/sizeof(array[0]))

void maxsubarray(int *array, int len, int *, int *);

main(){

         int array[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7};

         int i, start, end;

         printf("原始数组:\n");

         for (i = 0; i < LEN(array); i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         maxsubarray(array, LEN(array), &start, &end);

         printf("最大子序列的起始位置为:%d, 终止位置为:%d\n", start+1, end+1);

         printf("最大子序列为: ");

         for (i = start; i <= end; i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         putchar(‘\n‘);

}

void maxsubarray(int *array, int len, int *start, int *end){

         int i, j, sum, maxsum;

         maxsum = 0;

         for (i = 0; i < len; i++){

                   sum = 0;

                   for (j = i; j < len; j++){                          

                                     sum += array[j];

                                     if (sum > maxsum){

                                               maxsum = sum;

                                               *start = i;

                                               *end = j;

                                     }                                            

                   }

         }

         printf("\n子序列的求和最大值为: %d\n", maxsum);             

}

运行时间为θ(nlgn)

使用分治法求解:

假设我们要寻找子数组A[low..high]的最大子数组,使用分治技术意味着要将子数组划分为两个规模尽量相等的子数组,也就是说,要找到子数组的中央位置mid,然后考虑求解两个子数组A[low..high]的任何连续子数组A[i..j]所处的位置必然是以下三种情况之一:

  1. 完全位于子数组A[low, mid]中,因此,low <= i <= j <= mid
  2. 完全位于子数组A[mid+1, high]中,因此mid <= i <= j <= high
  3. 跨越了重点,因此low <= i <= mid < j <= high

递归地求解A[low..mid]和A[mid+1..high]的最大子数组,因为这两个子问题仍然是最大子数组问题,只是规模更小

伪代码

注意求出跨越中点的最大子数组问题并非原问题的更小实例,因为它加入了限制,求出的子数组必须跨越中点,任何跨越中点的子数组都由两个子数组A[i..mid]和A[mid+1..j]组成,其中low<= i <= mid且mid< j <= high,因此,只需要找出形如A[i..mid]和A[mid+1..j]的最大子数组,然后将其合并即可,过程FIND-MAX-CORSSING-SUBARRAY接收数组A和下标low, mid和high为输入,返回一个下标元组划定跨越中点的最大子数组的边界,并返回最大子数组中值的和

先求出左半部A[low..mid]的最大子数组,再求出右半部A[mid+1..high]的最大子数组,然后返回子数组A[max-left..max-right]

  FIND-MAX-CORSSING-SUBARRAY(A, low, mid, high)

         left-sum = A[mid]

         sum = 0

         for i = mid downto low

                   sum = sum + A[i]

                   if sum > left-sum

                            left-sum = sum

                            max-left = i

         right-sum = A[mid+1]

         sum = 0

         for j = mid+1 to high

                   sum += A[j]

                   if sum > right-sum

                            right-sum = sum

                            max-right = j

         return (max-left, max-right, left-sum+right-sum)

求解最大子数组问题的分治算法的伪代码:

FIND-MAXIMUM-SUBARRAY(A, low, high)

         if high == low

                   return(low, high, A[low])

         else mid = (low + high) / 2

                   (left-low, left-high, left-sum) = FIND-MAXIMUM-SUBARRAY(A, low, mid)

                   (right-low, right-high, right-sum) = FIND-MAXIMUM-SUBARRAY(A, mid+1, high)

                   (cross-low, corss-high, cross-sum) = FIND-MAX-CROSSING-SUBARRAY(A, low, mid, high)

                   if left-sum >= right-sum and left-sum >= corss-sum

                            return (left-low, left-high, left-sum)

                   else if right-sum >= left-sum and right-sum >= cross-sum

                            return (right-low, right-high, right-sum)

                   else

                            return (corss-low, cross-high, cross-sum)

C语言代码实现

#include<stdio.h>

#define LEN(array) (sizeof(array)/sizeof(array[0]))

int maxsubarray(int *, int , int , int *, int *);

int crosssubarray(int *array, int low, int mid, int high, int *begin, int *end);

main(){

         int array[] = {13, -3, -25, 20, -3, -16, 23, -18, -20, -7, 12, -5, -22, 15, -4};

         int i, start, end, sum;

         printf("原始数组:\n");

         for (i = 0; i < LEN(array); i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         sum = maxsubarray(array, 0, LEN(array)-1, &start, &end);

         printf("最大子序列的和为:%d\n", sum);

         printf("最大子序列的起始位置为:%d, 终止位置为:%d\n", start+1, end+1);

         printf("最大子序列为: ");

         for (i = start; i <= end; i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         putchar(‘\n‘);

}

int maxsubarray(int *array, int low, int high, int *begin, int *end){

         int mid, left_sum, right_sum, cross_sum;

         int left_low, left_high, right_low, right_high, cross_low, cross_high;

         if (low == high){

                   return array[low];

                   *begin = low;

                   *end = high;

         }

         mid = (low + high) / 2;

         left_sum = maxsubarray(array, low, mid, begin, end);

         left_low = *begin;

         left_high = *end;

         right_sum = maxsubarray(array, mid+1, high, begin, end);

         right_low = *begin;

         right_high = *end;

         cross_sum = crosssubarray(array, low, mid, high, begin, end);

         cross_low = *begin;

         cross_high = *end;

         if (left_sum > right_sum && left_sum > cross_sum){

                   *begin = left_low;

                   *end = left_high;

                   return left_sum;

         }

         else if (right_sum > left_sum && right_sum > cross_sum){

                   *begin = right_low;

                   *end = right_high;

                   return right_sum;

         }                

         else{

                   *begin = cross_low;

                   *end = cross_high;

                   return cross_sum;             

         }

}

int crosssubarray(int *array, int low, int mid, int high, int *begin, int *end){

         int left_sum, right_sum, sum, i, j;

         left_sum = array[mid];

         sum = 0;

         *begin = mid;

         *end = mid+1;

         for (i = mid; i >= low; i--){

                   sum += array[i];

                   if (sum > left_sum){

                            left_sum = sum;

                            *begin = i;

                   }

         }

         right_sum = array[mid+1];

         sum = 0;

         for (j = mid+1; j <= high; j++){

                   sum += array[j];

                   if (sum > right_sum){

                            right_sum = sum;

                            *end = j;

                   }

         }

         return left_sum + right_sum;

}

运行时间为θ(n)

只使用了一次for循环,注意,该算法只适用于数组原来的所有元素的求和值大于0,如果原来的数组的所有元素的求和值小于0,则不适用于本算法

C语言代码实现

#include<stdio.h>
#define LEN(array) (sizeof(array)/sizeof(array[0]))

void maxsubarray(int *array, int len, int *, int *);

main(){

//      int array[] = {13, -3, -25, 20, -3, -16, 23, -18, -20, -7, 12, -5, -22, 15, -4};

//      int array[] = {13, -3, -25};

         int array[] = {13, -3, -25, 55, -34};

         int i, start, end;

         printf("原始数组:\n");

         for (i = 0; i < LEN(array); i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         maxsubarray(array, LEN(array), &start, &end);

         printf("最大子序列的起始位置为:%d, 终止位置为:%d\n", start+1, end+1);

         printf("最大子序列为: ");

         for (i = start; i <= end; i++)

                   printf("%5d", array[i]);

         putchar(‘\n‘);

         putchar(‘\n‘);

}

void maxsubarray(int *array, int len, int *start, int *end){

         int i, sum, maxsum, temp;      

         sum = maxsum = 0;

         temp = 0;

         for (i = 0; i < len; i++){

                   sum += array[i];

                   if (sum > maxsum){

                            maxsum = sum;

                            *end = i;

                            *start = temp;
                   }

                   else if (sum < 0){

                            //temp的值就是最大子序列的起始位置

                            sum = 0;

                            temp = i + 1;
                   }

         }       

         printf("\n子序列的求和最大值为: %d\n", maxsum);             

}

最大子序列问题

时间: 2024-10-01 11:14:54

最大子序列问题的相关文章

14-高效求最长公共子序列(二维数组存不下)

/*                                   See LCS again时间限制:1000 ms  |  内存限制:65535 KB难度:3 描述 There are A, B two sequences, the number of elements in the sequence is n.m; Each element in the sequence are different and less than 100000. Calculate the length

POJ 2533 - Longest Ordered Subsequence(最长上升子序列) 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:http://poj.org/problem?id=2533 Description A numeric sequence of ai is ordered if a1 < a2 < ... < aN. Let the subsequence of the given numeric sequence (a1, a2, ..., aN) be any sequence (ai1, ai2, ..., aiK)

最大连续子序列和

对于给定的数组 numnum,一个长度为 ss 的连续子序列是指由 num_i,num_{i+1},num_{i+2}\ldots,num_{i+s-1}num?i??,num?i+1??,num?i+2??…,num?i+s−1?? 组成的序列.数组中的元素有可能为正数.负数或 00.你需要从数组中找出元素总和最大的一个连续子序列. 比如,对于数组 1,-3,2,6,-5,81,−3,2,6,−5,8,其最大连续子序列之和是 2+6-5+8=112+6−5+8=11. 对于一段区间内的最大连续

最长公共子序列的代码实现

关于最长公共子序列(LCS)的相关知识,http://blog.csdn.net/liufeng_king/article/details/8500084 这篇文章讲的比较好,在此暂时不再详说. 以下是我代码实现两种方式:递归+递推: 1 #include <bits/stdc++.h> 2 using namespace std; 3 int A[100]; 4 int B[100]; 5 6 //int B[]={2,3,5,6,9,8,4}; 7 int d[100][100]={0};

hdu1231 最大连续子序列

最大连续子序列 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 22849    Accepted Submission(s): 10135 Problem Description 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j

计算数组的最大子序列之和

问题描述: 计算一个给定数组的最大子序列之和 分析: 有三种方法: 1,扫描3遍,可以计算所有的子序列之和,但是复杂度为N^3. 2,扫描2遍,计算以任意元素开始的和,如果大于当前的最大值则将最大值付给它,复杂度为N^2. 3,扫描一遍,计算任意元素开始的值,如果小于零则清零,否则继续往后加. 代码实现: package c02; /**  * @project: DataStructureAndAlgorithmAnalysis  * @filename: MaxSubSum  * @vers

HDU 3998 Sequence (最长递增子序列+最大流SAP,拆点法)经典

Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1666    Accepted Submission(s): 614 Problem Description There is a sequence X (i.e. x[1], x[2], ..., x[n]). We define increasing subsequ

LintCode-乘积最大子序列

找出一个序列中乘积最大的连续子序列(至少包含一个数). 样例 比如, 序列 [2,3,-2,4] 中乘积最大的子序列为 [2,3] ,其乘积为6. 分析:访问到每个点的时候,以该点为子序列的末尾的乘积,要么是该点本身,要么是该点乘以以前一点为末尾的序列,注意乘积负负得正,故需要记录前面的最大最小值. 代码: class Solution { public: /** * @param nums: a vector of integers * @return: an integer */ int m

算法面试题 之 最长递增子序列 LIS

找出最长递增序列 O(NlogN)(不一定连续!) 参考 http://www.felix021.com/blog/read.php?1587%E5%8F%AF%E6%98%AF%E8%BF%9E%E6%95%B0%E7%BB%84%E9%83%BD%E6%B2%A1%E7%BB%99%E5%87%BA%E6%9D%A5 我就是理解了一下他的分析 用更通俗易懂的话来说说题目是这样 d[1..9] = 2 1 5 3 6 4 8 9 7 要求找到最长的递增子序列首先用一个数组b[] 依次的将d里面

NYOJ 36 &amp;&amp;HDU 1159 最长公共子序列(经典)

链接:click here 题意:tip:最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列. 输入 第一行给出一个整数N(0<N<100)表示待测数据组数 接下来每组数据两行,分别为待测的两组字符串.每个字符串长度不大于1000. 输出 每组测试数据输出一个整数,表示最长公共子序列长度.每组