题目名称:一维数组的最大子数组和
时间限制:1000ms内存限制:256M
题目描述
下面是一个一维数组的 “最大子数组的和” 的动态规划的解法
# include <iostream>
# include <stdio.h>
# include <string.h>
int MaxSum(int* arr, int size)
{
    int current = arr[0]; //current max sum
    int max = current;
    for (int i = 0; i < size; i++)
    {
        if (current < 0)
            current = 0;
        current += arr[i];
        if (current > max)
            max = current;
    }
    return max;
}
int main(void)
{
    char x[40], y[40];
    int a1[5] = { -1, 5, 6, -7, 3 };
    int a2[5] = { -5, -4, -8, -1, -10 };
    int a3[5] = { -1, 5, 6, -7, 10 };
    int max1, max2, max3;
    max1 = MaxSum(a1, 5);
    max2 = MaxSum(a2, 5); //这个应该返回 -1, 
    max3 = MaxSum(a3, 5);
}
如果我需要返回值返回这个最大子数组的开始和结束的下标,你要怎么修改这个程序?
输入描述:
第一行输入一个整数n,表示数组长度(n < 100000) 第二行输入数组元素
输出描述:
输出和最大的子数组的开始下标、结束下标
示例
示例1
输入
9
-2 1 -3 4 -1 2 1 -5 4
输出
3 6
提示
解释:连续子数组 [4,-1,2,1] 的和最大,该子数组在原数组中开始下标为 3, 结束下标为 6。
刚看到这个题目的时候,先想的是暴力算法,直接穷举,用递归枚举出所有的连续片段和,取最大的。。。。嗯,结果大家都能想的到,肯定无法通过
这里放出一个提交的用例测试
输入
1000
-6 7 -4 2 -5 2 -4 -7 -3 -9 7 0 7 -8 -7 -10 -5 6 1 -8 9 5 4 6 8 6 -5 -6 -3 5 9 -10 -3 -2 -4 -4 -3 -8 7 5 -6 1 5 -8 -5 6 4 -1 -2 -3 -6 4 -5 -8 8 5 7 1 3 5 9 -6 0 6 0 -7 1 8 0 -6 -6 -3 -1 -4 -2 -10 -2 6 4 -5 -6 0 0 2 6 -4 -6 2 3 -4 7 7 -8 -6 -1 -9 -9 -2 3 -6 -10 -6 9 -5 9 4 5 -3 2 0 3 -2 5 -9 -3 -5 -3 -3 -8 -8 -9 2 0 -4 -6 -5 5 4 -2 -3 7 -6 7 -9 2 -3 -3 -5 4 -5 6 -6 -2 9 -9 5 1 -4 -9 -8 5 1 -6 1 2 8 9 -6 -8 7 -3 -7 7 9 5 -5 6 -10 6 7 -1 -7 6 -7 9 2 3 -9 -1 6 9 -9 1 -6 4 -2 2 9 2 -9 4 5 9 -3 -8 3 7 5 5 9 -4 7 -8 2 3 5 2 -3 -8 4 -6 6 0 -9 8 7 9 4 -10 3 1 -5 8 3 -9 6 4 -8 1 1 0 -10 5 -1 -7 9 3 -6 2 5 -7 -7 7 -5 -4 9 -2 -4 5 -8 6 9 5 -8 -9 -2 1 3 -9 -8 0 2 2 -1 0 4 -2 -10 -3 3 7 -4 1 -4 -7 5 1 9 9 -8 -3 2 -2 -7 9 -7 -8 -10 0 9 9 9 -5 -7 5 5 -9 2 4 -6 -7 5 4 6 9 6 0 3 2 7 -4 5 -6 8 1 -2 -6 -10 -1 -9 4 9 -1 -9 2 -8 -8 -2 -7 6 -5 -1 8 8 -5 -10 -8 -3 1 -1 -3 -7 7 -7 -9 1 2 7 6 -8 -1 3 -2 -6 9 -1 -7 -10 5 7 -4 4 2 4 -2 1 4 8 8 -6 -5 5 -7 -6 9 3 -1 0 9 6 7 7 -7 -8 6 9 -1 -6 7 9 -9 4 0 -2 -7 7 6 6 9 0 -8 9 -10 -10 8 -10 -6 9 8 -6 -9 6 7 4 8 -7 -10 6 -7 7 2 5 0 -6 -2 5 -6 -1 -6 8 -10 -9 5 -7 -9 -4 2 -2 -2 1 -4 -5 6 9 5 3 -7 -6 -6 -9 -6 -1 1 9 -3 -10 8 3 -7 2 4 -6 -1 4 -5 8 3 5 -8 -7 -2 -7 1 -7 6 -8 0 1 -8 -10 6 2 -4 -3 7 1 -6 5 7 -3 -7 3 1 4 4 7 -1 0 7 -1 8 -4 4 9 -10 3 3 7 2 -2 4 -8 5 1 -3 -8 -8 7 0 -1 -4 0 7 4 6 5 -2 -7 -9 1 -9 1 -4 -7 0 -10 -5 -4 4 1 -2 2 8 -9 5 -5 -6 4 9 6 4 -6 -8 -1 8 8 4 3 -6 -4 0 -4 2 -1 -3 7 0 -10 4 6 7 -7 -10 4 -1 7 -4 0 -10 6 4 -9 -8 4 0 -1 6 -10 5 -9 -1 -2 -2 -1 1 -2 -9 -10 -6 8 -10 -9 -2 -7 -9 8 9 -4 8 7 4 -6 -8 4 -10 9 8 9 -5 -4 -2 1 8 -7 1 -8 5 -8 3 9 -6 -1 -5 -4 -2 9 0 2 -8 7 -6 -5 3 2 -7 1 -6 7 2 -9 1 0 -8 -7 -6 4 -5 4 0 9 9 -3 5 -10 1 2 8 -9 -5 3 -9 5 -10 -10 -1 -4 8 -6 7 1 -5 -7 9 6 -6 6 3 -2 -10 8 7 1 -3 2 -8 0 7 9 -9 1 7 4 9 7 -1 4 -1 -3 -8 1 -1 6 0 -5 3 1 -7 -9 5 -10 6 6 9 8 -1 1 -7 8 0 -1 2 1 -2 -6 0 -8 -9 -5 1 -7 8 5 7 -10 -7 5 0 3 3 8 2 -3 -1 9 -1 0 -3 9 -5 0 -1 8 -3 -10 -4 9 -1 7 1 -1 -6 1 3 1 -1 0 2 -3 -1 -2 2 -7 -1 9 6 -1 4 4 5 4 -10 -8 -8 8 -6 5 -6 -10 6 3 -8 -4 1 7 -3 -10 -4 2 -8 -8 -6 -7 8 2 -9 2 0 6 5 3 -7 6 -2 3 5 -5 0 -7 -6 -1 9 6 -6 -7 9 -8 -9 3 -5 -5 -7 7 9 -5 -4 -1 -10 2 9 4 -2 -2 0 -5 5 -6 3 -9 -9 3 4 9 4 2 0 -3 -6 -5 -8 -1 -6 7 5 -3 1 -4 -8 3 2 6 3 2 -10 -5 -8 -6 -3 9 7 6 -6 -3 -7 0 8 -8 7 8 -6 -1 -9 -9 -7 0 -6 -8 -9 6 5 -10 -6 4 0 -10 9 1 -3 0 4 -7 -7 3 9 -4 -1 -1 3 2 -9 -6 -4 -9 5 -10 -3 2 0 0 6 0 -4 -4 1 -6 -10 5 -6 -1 -6 -3 -4 2 9 7 -1 -8 -7 1 -2 -1 -10 -1 -6 -5 7 8 -10 -5 6 -6 -6 9 1 4 -10 -5 5 2 7 0 2 7 5 0 8 9 7 -2 -7 9 -7 -5 -9 4 4 9 -2 7 2 9
呵呵,这要是递归出来,算法复杂度该是多少
然后,老顾当初还没看到背包问题,就自己琢磨了一个办法
将连续的非负整数合并成一个正数,将连续的非正正数合并成一个负数,并记录下合并时的位置信息
 然后将首尾两端的负数去掉
 例如
10
-3 -7 10 -3 -4 11 12 -4 3 -2
合并后
-10 10 -7 23 -4 3 -2
去掉首尾的负数
10 -7 23 -4 3
然后再枚举也还是很复杂,于是老顾有补充了一个办法,将连续三个数,两边绝对值大于中间数的,合并
10 -7 23 -4 3
合并成
23 -4 3
好像数组中还是很多,嗯再补充一个办法
 数组中前两个值得和为负数,或者最后两个数的和为负数的也干掉
嗯,这样,经过递归,最后返回的值就一个了。。。。直接返回了23,取得保存的相应的位置信息,就可以得到题目要求的返回值了
这个办法。。。怎么说呢,是一个土办法,额外记录的东西太多了,有点累赘,算法上倒是复杂度比较低,最后得到了50%的通过率,嗯这里主要存在一个问题,就是最大和的前边有和为零的正负数组合时,我这边是没有放到序列里的,懒得改了。。。
先放出这个土办法的代码
# 请关闭中文输入法,用英文的字母和标点符号。
# 如果你想运行系统测试用例,请点击【执行代码】按钮,如果你想提交作答结果,请点击【提交】按钮,
# 注意:除答案外,请不要打印其他任何多余的字符,以免影响结果验证
# 本OJ系统是基于 OxCoder 技术开发,网址:www.oxcoder.com
# 模版代码提供基本的输入输出框架,可按个人代码习惯修改
class Solution:
    def __init__(self) -> None:
        self.printAble = False
        pass
    
    def comp(self,n,arr,m,f,t):
        if n<3:
            return arr,m,f,t
        x = arr
        if x[-1]['s']+x[-2]['s']<=0 and x[-1]['s']<m:
            if self.printAble:
                print('removed end:',x[-1],x[-2])
            x.pop(-1)
            x.pop(-1)
            return x,m,f,t
        if x[0]['s']+x[1]['s']<=0 and x[0]['s']<m:
            if self.printAble:
                print('removed head:',x[0],x[1])
            x.pop(0)
            x.pop(0)
            return x,m,f,t
        l = len(x)
        s = 0
        while True:
            if s+2<len(x):
                if x[s]['s']>0 and x[s]['s']+x[s+1]['s']>0 and x[s+2]['s']+x[s+1]['s']>0:
                    x[s]['e']=x[s+2]['e']
                    x[s]['a']+=x[s+1]['a']+x[s+2]['a']
                    x[s]['s']=sum(x[s]['a'])
                    if m<x[s]['s']:
                        m=x[s]['s']
                        f=str(x[s]['b'])
                        t=str(x[s]['e']-1)
                    if self.printAble:
                        print('comp++:{},{}\n{}'.format(x[s+1],x[s+2],x[s]))
                    x.pop(s+1)
                    x.pop(s+1)
                if x[s]['s']<0 and abs(x[s]['s'])>=x[s+1]['s'] and abs(x[s+2]['s'])>=x[s+1]['s']:
                    x[s]['e']=x[s+2]['e']
                    x[s]['a']+=x[s+1]['a']+x[s+2]['a']
                    x[s]['s']=sum(x[s]['a'])
                    if self.printAble:
                        print('comp--:{},{}\n{}'.format(x[s+1],x[s+2],x[s]))
                    x.pop(s+1)
                    x.pop(s+1)
                    
                    
                s+=1
            else:
                if l==len(x):
                    break
                else:
                    l=len(x)
                    s=0
        return x,m,f,t
    
    def solution(self, n, arr):
        result = None
        # TODO: 请在此编写代码
        
        x = [{'b':0,'e':1,'s':arr[0],'a':[arr[0]]}]
        m = 0
        rx = ''
        ry = ''
        for i in range(1,n):
            if arr[i]>=0 and x[-1]['s']>0:
                x[-1]['e']=i+1
                x[-1]['s']+=arr[i]
                x[-1]['a'].append(arr[i])
            elif arr[i]<=0 and x[-1]['s']<0:
                x[-1]['e']=i+1
                x[-1]['s']+=arr[i]
                x[-1]['a'].append(arr[i])
            else:
                x.append({'b':i,'e':i+1,'s':arr[i],'a':[arr[i]]})
            if m<x[-1]['s']:
                m = x[-1]['s']
                rx = str(x[-1]['b'])
                ry = str(x[-1]['e']-1)
                
        if x[0]['s']<0:
            x.pop(0)
        if x[-1]['s']<0:
            x.pop(-1)
        
        while True:
            l = len(x)
            x,m,rx,ry = self.comp(l,x,m,rx,ry)
            if l == len(x):
                break
        if self.printAble:
            print('\n------\n')
            print(x)
        result = [rx,ry]
        inp = ','.join([str(t) for t in arr])
        
        return result
if __name__ == "__main__":
    n = int(input().strip())
    
    
    arr = [int(item) for item in input().strip().split()]
    
    
    sol = Solution()
    result = sol.solution(n, arr)
    print(" ".join(result))

后来,在每日一练中,碰到了一个背包问题,然后被这个算法惊了呆
用了一个二维数组,就将所有可能的最大价值弄出来了。。。。。很不错
为了锻炼孩子的耐心和细心,我还把这个办法教给了孩子,然后和孩子一起填表格玩。。。。。
然后,又在每日一练的选择题里,碰到了一个求一个数组中连续片段最大积的问题。。。。竟然也是用一个二维表格完成的。。。。虽然没要求输出位置,但是很有启发啊!!!
于是,老顾自己也来个二维表格求连续最大和,以及他的位置!!!
    def solution(self, n, arr):
        result = []
        
        z = [i for i in arr if i>0]
        if len(z)<2:
            mx = max(arr)
            return [str(arr.index(mx)),str(arr.index(mx))]
        
        l = len(arr)
        dp = [[0]*(l+1) for _ in range(n)]
        dp[0][0] = arr[0]
        for i in range(1,l):
            dp[i][0] = arr[i]
            for j in range(i):
                dp[i][j+1] = arr[i] + dp[i-1][j]
        rows = [max(row) for row in dp]
        mx = max(rows)
        row = rows.index(mx)
        nums = dp[row].count(mx)
        col = dp[row].index(mx)
        while nums>1:
            col = dp[row].index(mx,col+1)
            nums-=1
        result = [str(row-col),str(row)]
既然二维数组这么好用,那么我也来一次,第一列放数组本身的数字
 第二列放与之前的数的和
arr[0]
arr[1] arr[0]+arr[1]
arr[2] arr[1]+arr[2] arr[0]+arr[1]+arr[2]
…
弄成这么个二维数组后,直接看哪个数组的最大值最大,那么和最大的就在那个行里了,第一个下标就是结尾数字的位置了,然后如果出现了多次最大值,取靠后的那个,然后得到该最大值所在列的位置,然后行减去列,就是开始位置了。。。。这么一看,简直简单的一批。。。。土办法什么的真弱。。。。。
真是不学不知道,原来老顾已经落伍的这么严重了


















