ACM入门题-最大子矩阵暴力枚举-Go语言

16 篇文章 0 订阅

ACM入门题-最大子矩阵暴力枚举-Go语言

问题描述

我们这里所描述的矩阵即为一个n*m的二维数组,它的大小由数组内所有元素之和来决定。子矩阵则是一段上下左右连续的子数组,如图所示都为子矩阵。

image-20220404001722326

解决思路

最终的结果是一个标量,而我们在一个二维数组里寻找这个答案。所以要有降维打击的数学思想。

如果给我们的不是一个矩阵,而只是一个向量(一维数组),那我们就可把问题化解为最大连续子序列和问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j14sSo3x-1649008075884)(C:\Users\jackonessalad\AppData\Roaming\Typora\typora-user-images\image-20220404002652528.png)]

对于一维的数组而言我们可以动态规划

  • 状态:dp[i] 代表以 nums[i] 结尾的最大连续子序列和
  • 状态转移方程:dp[i] = max(nums[i]+dp[i-1],nums[i])
  • 结果:res = max(res,dp[i])
  • 实际上这个dp数组我们只用一次,且前一次添加的值,所以我们直接用一个整数变量来记录中间值即可。
  • tmp = max(nums[i]+tmp,nums[i]),res = max(res,tmp)

那我们如何把这个解法扩展到二维数组呢?

我要先列出二维数组压缩成一维后的可能状态(以上下压缩来做):

image-20220404011103817

  • dp[i] = nums[1]+...+nums[i] 代表前 i 行之和
  • 那么任意几行之和就是 nums[j]+...+nums[i](i>j) = dp[i] - dp[j-1] = dpLine
  • 对于某几行之和中间数组 dpLine 再求最大连续子数组之和就是原矩阵的子矩阵之和

image-20220404012740332

image-20220404013218219

也就是说,我们接下来要对以上1+2+3+4=10个 dpLine 数组做最大连续子序列和的处理即可得到我们要的最大子矩阵。

代码实现

package main

import "fmt"

func main() {
	var n, m int
	fmt.Scan(&n, &m)
	a := make([][]int, n+1) // 二维数组行数都设为n+1,预留一行
	for i, _ := range a {
		a[i] = make([]int, m+1)
	}
	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			fmt.Scan(&a[i][j])
		}
	}
	dp := make([][]int, n+1) // dp[i] 表示a前i行和数组
	for i, _ := range dp {
		dp[i] = make([]int, m+1)
	}
	for i := 1; i <= n; i++ {
		for j := 1; j <= m; j++ {
			for k := 1; k <= i; k++ {
				dp[i][j] += a[k][j] // dp[i][j] = a[1][j]+a[2][j]+...+a[i][j]
			}
		}
	}
	res := -0x3f3f3f           // 结果
	tmp := 0                   // 保存可能的最大值
	dpLine := make([]int, m+1) // 存储a[i]~a[j]和的中间变量
	var max = func(a, b int) int {
		if a > b {
			return a
		}
		return b
	}
	for i := 1; i <= n; i++ {
		for j := 0; j < i; j++ {
			for k := 1; k <= m; k++ {
				dpLine[k] = dp[i][k] - dp[j][k] // a[i]~a[j]和
			}
			tmp = 0
			for k := 1; k <= m; k++ {
				// 转化为一维问题
				tmp = max(tmp+dpLine[k], dpLine[k])
				res = max(res, tmp)
			}
		}
	}
	fmt.Println(res)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值