[蓝桥杯][历届试题 PREV-26]最大子阵(Java)(动态规划-最大子矩阵)

本文介绍了一种解决历届试题中最大子矩阵和问题的算法,通过将二维矩阵压缩为一维数组,利用类似求一维数组最大连续和的方法,找到矩阵中元素和最大的子矩阵。

历届试题 最大子阵  

时间限制: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。


解题思路:

想想类似的题型,求一个一维数组的最大连续和,这个应该是不难的。代码如下:

        //max是最后结果,arr是数组,m是arr数组长度
		int max = Integer.MIN_VALUE;
		void getMax(int[] arr, int m) {
			int temp = 0;
			for(int mi=0;mi<m;++mi) {
				temp += arr[mi];
				max = max>temp?max:temp;
				if(temp < 0)temp = 0;
			}
		}

现在的情况就是把一维拓展成了二维数组,因为最后要求的是矩阵,也就是说只要把矩阵压缩成一个一维数组就好了,三层循环:1、起始的行数,2、终止的行数,3、每列。然后把每次生成的一位数组求最大连续和,找到所有max的最大值。

说起来好像也没用到动态规划。。。。

反思错误:

①幸亏这题的数据量不大,不然我的方法绝对超时【O(m n^2)】

②因为题型是动态规划,一直向动态规划的想法上靠,结果没想出来。我这解法也不算动态规划,挂羊头卖狗肉了。不过动态规划的最大子矩阵问题是可以这么解的。

Java代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args) {
//		long sta = System.nanoTime();

		InputStream is = System.in;
		OutputStream os = System.out;

		IN cin = new IN(is);
		PrintWriter cout = new PrintWriter(os);

		SO so = new SO();
		so.solution(cin, cout);

//		long end = System.nanoTime();
//		cout.println("耗时:" + (double)(end-sta)/1e6 + "ms");

		cout.close();
	}

	
	
	
	
	static final int MOD = (int)1e9 + 7;
	//实际代码开始的类--------------------------------------------------
	static class SO {
		int max = Integer.MIN_VALUE;
		void getMax(int[] arr, int m) {
			int temp = 0;
			for(int mi=0;mi<m;++mi) {
				temp += arr[mi];
				max = max>temp?max:temp;
				if(temp < 0)temp = 0;
			}
		}
		void solution(IN cin, PrintWriter cout) {
			int n = cin.nextInt(), m = cin.nextInt();
			int[][] mat = new int[n][m];
			int[] arr = new int[m];
			
			for(int ni=0;ni<n;++ni) {
				for(int mi=0;mi<m;++mi) {
					mat[ni][mi] = cin.nextInt();
				}
			}
			
			for(int sni=0;sni<n;++sni) {
				Arrays.fill(arr,0);
				for(int eni=sni;eni<n;++eni) {
					for(int mi=0;mi<m;++mi) {
						arr[mi] += mat[eni][mi];
					}
					getMax(arr,m);
				}
			}
			
			cout.println(max);
		}//end solution
	}//end SO
	
	
	
	
	
	
	
	
	
	//以下是快读部分
	static class IN {
		private BufferedReader reader;
		private StringTokenizer tokenizer;

		IN(InputStream is) {
			reader = new BufferedReader(new InputStreamReader(is), 32768);
			tokenizer = null;
		}
		
		public String next() {
			while (tokenizer == null || !tokenizer.hasMoreTokens()) {
				try {
					tokenizer = new StringTokenizer(reader.readLine());
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			return tokenizer.nextToken();
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

		public long nextLong() {
			return Long.parseLong(next());
		}

		public double nextDouble() {
			return Double.parseDouble(next());
		}

	}
}

测试结果:

### 关于蓝桥杯竞赛中的最大子矩阵问题 #### 子矩阵最大累加算法分析 对于子矩阵最大累加这一类题目,核心挑战在于如何高效地遍历所有可能的子矩阵并找到其中具有最大累加的那个。直接枚举所有的子矩阵组合显然不是一种有效的方法,因为随着矩阵尺寸的增长,这种暴力搜索的时间复杂度会急剧增加。 为了优化这个问题,可以采用降维策略来简化计算过程。具体来说,可以通过先对每一累积的方式将二维问题转化为一系的一维问题处理[^1]。这样做的好处是可以利用已经成熟的动态规划技术快速定位到最优解的位置。 当把原始矩阵转换成多个一维序之后,就可以应用经典的 Kadane's Algorithm 来查找这些序里的连续数组拥有最大的总。该算法能够在 O(n) 的时间复杂度内完成任务,从而大大提高了整体效率。 ```python def max_subarray_sum(nums): current_max = global_max = nums[0] for num in nums[1:]: current_max = max(num, current_max + num) if current_max > global_max: global_max = current_max return global_max ``` 接着,在实际编码过程中还需要注意边界条件以及输入数据类型的验证等问题以确保程序健壮性准确性。 #### 使用前缀与双指针优化解决方案 除了上述提到的方法外,另一种常见的技巧是通过构建二维前缀表配合双指针扫描来进更高效的解答。这种方法能够进一步减少不必要的重复运算次数,特别是在面对较大规模的数据集时表现尤为突出[^2]。 创建一个额外的空间存储从起点至当前位置为止的所有元素之和作为辅助结构;随后固定上下两条边界的坐标位置形成窗口,并在这个范围内移动左右两侧端点直至覆盖整个宽度范围内的每一个潜在候选区域。期间不断更新记录当前已知的最佳结果直到结束循环即可获得最终答案。 ```python from itertools import accumulate def max_sum_submatrix(matrix, k=None): # 如果给定k,则表示不超过k的最大子矩阵 rows, cols = len(matrix), len(matrix[0]) result = float('-inf') for l in range(cols): row_sums = [0] * rows for r in range(l, cols): for i in range(rows): row_sums[i] += matrix[i][r] accu_list = list(accumulate(row_sums)) if not k: # 不限k的情况下直接找最大值 temp_result = max_subarray_sum(accu_list) result = max(result, temp_result) else: # 需要满足<=k约束条件下寻找最佳方案 prefix_set = set([0]) curr_accu = 0 for val in accu_list: curr_accu += val prev = next((x for x in sorted(prefix_set, reverse=True) if (curr_accu-x)<=k), None) if prev is not None and ((curr_accu-prev)>result or result==float('-inf')): result = min(k, curr_accu - prev) prefix_set.add(curr_accu) return result ``` 此段代码实现了基于前缀与双指针相结合的方式来解决问题,既考虑到了一般情况下的无限制最值场景也兼顾了特定数值上限的要
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值