蓝桥杯历年试题JAVA——最大矩阵和

12 篇文章 0 订阅
9 篇文章 0 订阅

题目: 

历届试题 最大子阵  

时间限制:1.0s   内存限制:256.0MB

问题描述

  给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。

  其中,A的子矩阵指在A中行和列均连续的一块。

输入格式

  输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
  接下来n行,每行m个整数,表示矩阵A。

输出格式

  输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。

样例输入

3 3
-1 -4 3
3 4 -1
-5 -2 8

样例输出

10

样例说明

  取最后一列,和为10。

数据规模和约定

  对于50%的数据,1<=n, m<=50;
  对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。

思路: 

可以参照求解最大子序列和的问题时算法(动态规划),把二维数组转为一维数组求最大值。

行、列必须输连续的,那么可以先求出每一列的前n项和,存入sum[]数组,然后再按照动态规划思想,求每一列的最大和

对应dp[]有
                             //如果前一个dp值<=0,则不进行累加(加上负值肯定变小)
                            dp[j] = sum[j];               dp[j-1]<=0
                            //如果>0,则进行累加(即当前列加上前一列)
                            dp[j] = dp[j-1]+sum[j];     dp[j-1]>0

如样例:

 

那么,从这张图,我们可以看出,第i行的和=前i-1行+第i行,动态规划思想,sum[]已经存储子问题的解。 

求解: 

第一次用了三个for,超时了。用两个for还是超时了。。。。

package 蓝桥练习;

import java.util.Arrays;
import java.util.Scanner;

/**
 * 思路:采用求解最大子序列和的问题时算法
 * 注意:把二维数组转为一维数组求解(A的子矩阵指在A中行和列均连续的一块。 )
 * @author lym
 *
 */

public class 最大矩阵和 {
	
	static Integer INF = Integer.MIN_VALUE;	//表示无穷小
	static int MAX = 505;
	static int[][] a = new int[MAX][MAX];
	static int[] sum = new int[MAX];		//存储第i列的前n行的和
	static int[] dp = new int[MAX];			//动态数组,将二维数组转为一维数组
	static int maxSum = INF;				//存储最大矩阵和
	
	
	static void fun(int n, int m) {
		for(int st=1; st<=n; st++) {	//st为开始行
			Arrays.fill(sum,0); 	//初始化sum数组,赋值为0
			for(int i=st; i<=n; i++) {	//i为结束行,从st行加到i行
				for(int j=1; j<=m; j++) {	//累加第j列的前n项和
					sum[j] += a[i][j];
					
					dp[1] = sum[1];		//设置动态数组的第一个值为sum[1],为了便于下面的比较
					if(dp[1] > maxSum)	//保证maxSum为最大值
						maxSum = dp[1];
					
					if(j==1)		//为1时,跳过下面的计算
						continue;
					else {
						if(dp[j-1] < 0)	//如果前一个dp值<=0,则不进行累加(加上负值肯定变小)
							dp[j] = sum[j];
						else					//如果>0,则进行累加(即当前列加上前一列)
							dp[j] = dp[j-1]+sum[j];
						if(dp[j] > maxSum)		//保证maxSum为最大值
							maxSum = dp[j];
					}
				}
			}
		}		
	}
	
	/*	三个for的写法,与两个for的差别不大
    static void fun(int n, int m) {
		for(int st=1; st<=n; st++) {	//st为开始行
			Arrays.fill(sum,0); 	//初始化sum数组,赋值为0
			for(int i=st; i<=n; i++) {	//i为结束行,从st行加到i行
				for(int j=1; j<=m; j++) {	//累加第j列的前n项和
					sum[j] += a[i][j];
				}
				dp[1] = sum[1];		//设置动态数组的第一个值为sum[1],为了便于下面的比较
				if(dp[1] > maxSum)	//保证maxSum为最大值
					maxSum = dp[1];
				for(int j=2; j<=m; j++) {	//求解最大段和
					if(dp[j-1] < 0)		//如果前一个dp值<=0,则不进行累加(加上负值肯定变小)
						dp[j] = sum[j];
					else					//如果>0,则进行累加(即当前列加上前一列)
						dp[j] = dp[j-1]+sum[j];
					if(dp[j] > maxSum)		//保证maxSum为最大值
						maxSum = dp[j];
				}
			}
		}		
	}*/

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner input = new Scanner(System.in);
		int n = input.nextInt();
		int m = input.nextInt();
		input.nextLine();
		for(int i=1; i<=n; i++)
			for(int j=1; j<=m; j++)
				a[i][j] = input.nextInt();
		fun(n, m);
		
		System.out.println(maxSum);
		
	}

}

运行结果:

这道题也想了蛮久的,参考了这位博客的思路:https://blog.csdn.net/huanhuanxiaoxiao/article/details/54924634

有大神有优化思路可以分享下,,,嘻嘻 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值