蓝桥杯JAVA-14.连续最大子数组模板

个人博客
www.tothefor.com
蓝桥杯复习知识点汇总

目录

分为连续子数组和非连续子数组,在一维的情况下可以等同于字串和子序列的最大和。

连续子数组

对于一个数A[ i ],若是A[ i ] 的左边累计数非负,那么加上A[ i ] 能使得值不小于A[ i ],认为累计值对整体和是有贡献的。如果前几项累计值负数,则认为有害于总和。

一维

动态规划:算法时间复杂度:O(n)

对于一个数A[ i ],若是A[ i ] 的左边累计数非负,那么加上A[ i ] 能使得值不小于A[ i ],认为累计值对整体和是有贡献的。如果前几项累计值负数,则认为有害于总和,应丢弃。

dp[ i ]表示以a[ i ]结尾的「连续子数组的最大和」,不是整个数组的最大子数组和

即:dp[ i ] = max{dp[ i-1 ] + a[ i ],a[ i ] };

初始状态:dp[0] = a[0];

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 200000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;


    public static void main(String[] args) throws Exception {

        int n = nextInt();
        int[] a = new int[maxd];
        int[] dp = new int[maxd]; //dp[i]表示以a[i]结尾的「连续子数组的最大和」
        for(int i=1;i<=n;++i) a[i] = nextInt();
        int ans = a[1]; //ans用来存储最后的答案
        dp[1]=a[1]; //初始化第一个数默认是它自己
        for(int i=1;i<=n;++i){ //如果a数组是从0开始存的,这里必须从第二个数开始,否则会存在下标为 0-1=-1 的情况.而现在这里从1或2开始都行
            dp[i]=Math.max(dp[i-1]+a[i],a[i]);
            ans=Math.max(ans,dp[i]);
        }
        System.out.println(ans);
        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers();
    }
    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception {
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }
}

测试数据:

//输入
9
-2 1 -3 4 -1 2 1 -5 4

//输出    
6
滚动数组(空间优化)

因为,dp[ i ] 只与dp[ i-1] 有关,所以可以用一个变量 midsum 来维护对于当前 dp[ i ]的dp[ i-1 ] 的值是多少,从而让空间复杂度降低到 O(1),这类似「滚动数组」的思想。

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 200000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    public static void main(String[] args) throws Exception {

        int n = nextInt();
        int[] a = new int[100]; //原数组
        for(int i=1;i<=n;++i) a[i] = nextInt();
        int midsum = a[1]; //初始化
        int ans = a[1]; //默认答案为第一个数
        for(int i=1;i<=n;++i){
            midsum=Math.max(midsum+a[i],a[i]);
            ans=Math.max(ans,midsum);
        }
        System.out.println(ans);
        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers();
    }
    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception {
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }
}

二维

将二维转化为一维数组计算,然后计算一维的最大子数组问题。

方法:将每一列求一个前缀和,得到一个一维数组。

时间复杂度:O(n*n*m)

矩阵转置

看网上的一些说明,当行大于列时进行转置(因为遍历的时候是遍历的行,所以找行、列中的较小值作为行可以减少遍历次数),但感觉根本没用,因为后面求的时候行和列都用到了,时间复杂度根本没变。但是这里也写上吧。

public static int[][] Matreverse(int[][] a,int m,int n) { //m行n列的数组a,a数组下标从1开始
    int[][] newArr = new int[n+5][m+5]; //转化后的n行m列数组
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            newArr[j][i] = a[i][j];
        }
    }
    return newArr;
}
代码实现

这里没有用转置。(感觉用了没用)

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
    public static Scanner sc = new Scanner(System.in);

    public static int maxd = 200000+7;
    public static int INF = 0x3f3f3f3f;
    public static int mod = 998244353;

    //求一维数组的最大子数组
    public static int getOne(int[]a,int n){ //n为长度,a数组是下标从1开始存储的
        int[] dp = new int[maxd]; //dp[i]表示以a[i]结尾的「连续子数组的最大和」
        int ans = a[1]; //ans用来存储最后的答案
        dp[1]=a[1]; //初始化第一个数默认是它自己
        for(int i=1;i<=n;++i){ //如果a数组是从0开始存的,这里必须从第二个数开始,否则会存在下标为 0-1=-1 的情况.而现在这里从1或2开始都行
            dp[i]=Math.max(dp[i-1]+a[i],a[i]);
            ans=Math.max(ans,dp[i]);
        }
        return ans;
    }

    public static void main(String[] args) throws Exception {

        int n = nextInt(); //行
        int m = nextInt(); //列
        int[][] a = new int[100][100]; //原数组
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                a[i][j] = nextInt();
            }
        }
        //求每一列的前缀和,每一列中从第一行到第i行的和
        int[][] p = new int[105][105]; //p[i][j]表示j这一列的前i个数的和
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                p[i][j]=p[i-1][j]+a[i][j];
            }
        }

        int maxans = 0;
        for(int i=1;i<=n;++i){ //第i行
            for(int j=i;j<=n;++j){ //到最后一行
                maxans=Math.max(maxans,getOne(p[i],m));
            }
        }
        System.out.println(maxans);

        closeAll();
    }

    public static void cinInit(){
        cin.wordChars('a', 'z');
        cin.wordChars('A', 'Z');
        cin.wordChars(128 + 32, 255);
        cin.whitespaceChars(0, ' ');
        cin.commentChar('/');
        cin.quoteChar('"');
        cin.quoteChar('\'');
        cin.parseNumbers();
    }
    public static int nextInt() throws Exception {
        cin.nextToken();
        return (int) cin.nval;
    }
    public static long nextLong() throws Exception {
        cin.nextToken();
        return (long) cin.nval;
    }
    public static double nextDouble() throws Exception {
        cin.nextToken();
        return cin.nval;
    }
    public static String nextString() throws Exception {
        cin.nextToken();
        return cin.sval;
    }
    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }
}

测试数据:

//输入
2 3
1 2 3
3 -1 4

//输出
12
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值