最大子段和三种算法实现

问题描述

给定由 n 个整数(可能为负整数)组成的序列 a1、a2、……an,求该序列形如在这里插入图片描述
的子段和的最大值。当所有整数均为负整数时定义其最大子段和为 0。依次定义,所求的最优值为

max{
在这里插入图片描述

例如:当(a1,a2,a3,a4,a5,a6)=(-2,11,-4,13,-5,-2)时,最大子段和为在这里插入图片描述
=20。

三种算法求解

1、暴力算法

算法分析:

通过阅读了解问题后,可以初步尝试用最简单的方法解决问题,方便发现问题本质。最大子段和的结果很明显是通过:对数组排列组合得出子段和的个数,然后一一对比得出结果。

求子段和的个数需要两个循环:外循环是循环起点,内循环是循环终点。

求子段和对比结果需要一个循环。

三个for循环的时间复杂度是O(n3),然而在第二个for循环求处子段个数时就可以进行子段和比较了,省去最后一个for循环,使得算法时间复杂度为O(n2)。

算法实现:

算法伪代码:

在这里插入图片描述

2. 分治算法

算法分析:

可以从数组中间分开,结果要么在左边或右边,要么就在中间。结果在左边和右边的情况好办,那在中间的情况该如何分析呢?因为子段是连续的,所以结果只能从中心向两边扩,采用枚举法,左右走到尽头,结果相加就得到中间的最大解。所以该题采用分治算法的难度就可以解决了。

拆分子数组分别求得的最大子段和为sum1,sum2。时间复杂度为2T(N/2),从中心点向两边找最大的和,找到跨越的最大子段和的时间复杂度为O(n), 所以总体的时间复杂度为:T(n)=2T(n/2)+O(n) = O(nlogn)。

算法实现:

算法伪代码:

在这里插入图片描述

3. 动态规划

算法分析:

首先,该问题的解法符合动态规划的思想。

一、子问题重叠:无论问题规模怎么变化,最大子段和的子段都是在数组中。

二、原问题的最优解包含子问题的最优解:通过最简单的方法,我称之为暴力带值。列出小问题规模的结果,通过矩阵a[i]存入数值:

a[i]-211-413-5-2

在分治算法中,需要枚举n个数组,那如果从a[0]开始枚举,得到一段子段和是负数,那还需要用到该子段吗?显然不需要,直接从当前元素重新开始。

假定子段终点为a[j],那么之前的和必定为正且最大,设置b[i]数组记录a[j]结尾的全部子段的和,所以当b[j]>0时,b[j]=b[j-1]+a[j],否则b[j]=a[j],由此得到b[j]的动态规划递归式:

b[j]=max{b[j-1]+a[j],a[j]}, 1≤j≤n

b-2117201513
sum0117202020

算法实现:

算法伪代码:

在这里插入图片描述

上述算法显然时间复杂度时O(n),空间复杂度是O(n)。

算法结果

输出结果(取一次)

在这里插入图片描述

复杂度分析

三种算法在不同规模下运行时间如下表:

在这里插入图片描述

纵坐标单位为(ms),横坐标单位为(个)。

当问题规模到15000时,暴力算法明显变得笨重;当问题规=规模到达10000000时,分治算法也开始劣于动态规划。

三者的时间复杂度比较:O(n2)>O(nlogn)>O(n)。

源代码

暴力算法:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;

public class ziduanhe1 {
    public static int []a = new int[1000000];
    public static void test(int n){
        random_get(n);
        int ThisSum,MaxSum=0;
        for (int i = 0; i < n; i++) {
            ThisSum = 0;
            for (int j = i; j < n; j++) {
                ThisSum+=a[j];
                if (ThisSum>MaxSum){
                    MaxSum = ThisSum;
                }
            }
        }
        System.out.print(MaxSum);
    }
    public static int random_get(int l){
        Random r = new Random();
        int c = 40;
        for (int i = 0; i < l; i++) {
            a[i]= r.nextInt(c)%(2*c+1)-c/2;
        }

        return 0;
    }
    public static void main(String[] args){
        int n;
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            n = sc.nextInt();
//        for (int i = 0; i < n; i++) {
//            a[i] = sc.nextInt();
//        }
//        ziduanhe1 t = new ziduanhe1();
        long startTime = System.currentTimeMillis();
        SimpleDateFormat df = new SimpleDateFormat("\n" + "yyyy-MM-dd HH:mm:ss");
        System.out.println(df.format(new Date()));
        test(n);
        long endTime = System.currentTimeMillis();
        System.out.println(df.format(new Date()));
        System.out.println("\n" + "执行时间" + (endTime - startTime) + "ms");
        }
    }
}

分治算法:

import java.text.SimpleDateFormat;
import java.util.*;
public class ziduanhe2
{
    public static int []a =new int[101000000];
    public static int maxsum(int l,int r){
        if(l==r){
            return a[l];
        }
        int mid = (l+r)/2;
        int lsum = maxsum(l,mid);//左区间
        int rsum = maxsum(mid+1,r);//右区间
        int sum1=0,sum2=0;
        int lefts=0,rights=0;
        for(int i=mid;i>=l;i--){
            lefts+=a[i];
            if(lefts>sum1){
                sum1=lefts;
            }
        }
        for(int i=mid+1;i<=r;i++){
            rights+=a[i];
            if(rights>sum2){
                sum2=rights;
            }
        }
        int msum=sum1+sum2;
        return Math.max(Math.max(lsum,rsum),msum);
    }
    public static int random_get(int l){
        Random r = new Random();
        int c = 40;
        for (int i = 0; i < l; i++) {
            a[i]= r.nextInt(c)%(2*c+1)-c/2;
        }
        return 0;
    }
    public static void main(String[] args){
        int n;
        int ans;
        Scanner in=new Scanner(System.in);
        while(in.hasNext()){
            n=in.nextInt();
//            for(int i=1;i<=n;i++){
//                a[i]=in.nextInt();
//            }
            random_get(n);
            long startTime = System.currentTimeMillis();
            SimpleDateFormat df = new SimpleDateFormat("\n"+"yyyy-MM-dd HH:mm:ss");
            System.out.println(df.format(new Date()));
            ans = maxsum(0,n);
            if(ans<0){
                ans=0;
            }
            System.out.println(ans);
            long endTime = System.currentTimeMillis();
            System.out.println(df.format(new Date()));
            System.out.println("\n"+"执行时间"+(endTime-startTime)+"ms");
        }
    }
}

动态规划:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Scanner;

public class ziduanhe3 {
    public static int []a = new int[101000000];
    public static int maxSum(int n){
        random_get(n);
//        for (int i = 0; i < n; i++) {
//            System.out.print(a[i]);
//        }
        int sum = 0;
        int b = 0;
        for (int i = 0; i < n; i++) {
            if (b>0) b+=a[i];
            else b = a[i];
            if (sum<b) sum = b;
        }
        return sum;
    }
    public static int random_get(int l){
        Random r = new Random();
        int c = 40;
        for (int i = 0; i < l; i++) {
                a[i]= r.nextInt(c)%(2*c+1)-c/2;
            }
        return 0;
    }
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int n;
        int ans = 0;
        System.out.print("请输入元素个数:");
        while (sc.hasNext()){
            n =sc.nextInt();
//            for (int i = 0; i < n; i++) {
//                a[i] = sc.nextInt();
//            }
            long startTime = System.currentTimeMillis();
            SimpleDateFormat df = new SimpleDateFormat("\n"+"yyyy-MM-dd HH:mm:ss");
            System.out.println(df.format(new Date()));
            ans = maxSum(n);
            if (ans<0){
                ans = 0;
            }
            System.out.print("最大子段和:");
            System.out.print(ans);
            long endTime = System.currentTimeMillis();
            System.out.println(df.format(new Date()));
            System.out.println("\n"+"执行时间"+(endTime-startTime)+"ms");
        }
    }
}
  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
最长公共子序列问题和最大子段和问题都是动态规划问题。 1. 最长公共子序列问题 最长公共子序列问题(Longest Common Subsequence, LCS)是指给定两个序列X和Y,找出它们的最长公共子序列。 动态规划算法实现: 设X={x1,x2,…,xm}和Y={y1,y2,…,yn},令c[i][j]表示X的前i个字符和Y的前j个字符的LCS长度,b[i][j]记录c[i][j]是由哪一个子问题的解得到的。 则有以下的状态转移方程: c[i][j] = 0, i=0 或 j=0 c[i][j] = c[i-1][j-1]+1, i,j>0 且 xi=yj c[i][j] = max(c[i-1][j], c[i][j-1]), i,j>0 且 xi≠yj 实现代码如下: ```python def LCS(X, Y): m, n = len(X), len(Y) c = [[0]*(n+1) for i in range(m+1)] b = [[0]*(n+1) for i in range(m+1)] for i in range(1, m+1): for j in range(1, n+1): if X[i-1] == Y[j-1]: c[i][j] = c[i-1][j-1] + 1 b[i][j] = '↖' elif c[i-1][j] >= c[i][j-1]: c[i][j] = c[i-1][j] b[i][j] = '↑' else: c[i][j] = c[i][j-1] b[i][j] = '←' return c, b def printLCS(b, X, i, j): if i == 0 or j == 0: return if b[i][j] == '↖': printLCS(b, X, i-1, j-1) print(X[i-1], end='') elif b[i][j] == '↑': printLCS(b, X, i-1, j) else: printLCS(b, X, i, j-1) ``` 效率分析: 设X和Y的长度分别为m和n,时间复杂度为O(mn),空间复杂度也为O(mn)。 2. 最大子段和问题 最大子段和问题(Maximum Subarray Sum)是指在一个数列中找到一个连续的子序列,使得该子序列的和最大。 动态规划算法实现: 设a[1..n]为输入的数组,sum[i]表示以a[i]为结尾的最大子段和,则有以下的状态转移方程: sum[1] = a[1] sum[i] = max(sum[i-1]+a[i], a[i]), i>1 实现代码如下: ```python def maxSubarray(a): n = len(a) dp = [0] * n dp[0] = a[0] for i in range(1, n): dp[i] = max(dp[i-1] + a[i], a[i]) return max(dp) ``` 效率分析: 设a的长度为n,时间复杂度为O(n),空间复杂度为O(n)。 综上所述,最长公共子序列问题和最大子段和问题的动态规划算法都是时间复杂度为O(n^2),空间复杂度为O(n^2)的算法,但是两者的状态转移方程和实现方式不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值