矩阵游戏:给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。其中,A的子矩阵指在A中行和列均连续的一块。

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

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

输入描述:

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

输出描述:

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

解答:


二维前缀和:
首先,代码计算了一个二维前缀和数组 g[][]。对于数组 g 中的每个元素 g[i][j],它存储的是原矩阵从第一行到第 i 行,第一列到第 j 列的所有元素的和。

寻找最大子矩阵和:
接下来,代码使用三层嵌套循环来寻找最大子矩阵和。
外层循环枚举子矩阵的起始行 i。
第二层循环枚举子矩阵的结束行 j。
内层循环计算以 i 为起始行,j 为结束行的子矩阵的和 ans。


计算子矩阵和:
在内层循环中,ans 被用来累计当前子矩阵的和。每次迭代,ans 会加上 g[j][k] - g[i - 1][k],这实际上是计算了从第 i 行到第 j 行,第 k 列的子矩阵的和。
如果 ans 变为负数,则将其重置为0,因为负数会减少子矩阵的和。


如果当前子矩阵的和 ans 大于之前找到的最大和 sum,则更新 sum。

#include <iostream>
#include <string>
using namespace std;
int cal(int a[][502], int f[], int s[]) {
//	cout << "从" << f[0] << "," << f[1] << "到" << s[0] << "," << s[1] << "————————————" << endl;
	int all = 0;
	for (int i = f[0]; i <= s[0]; i++) {
		for (int j = f[1] ; j <= s[1]; j++) {
//			cout << "a[" << i << "][" << j << "]==" << a[i][j] << endl;
			all = all + a[i][j];

		}
//		cout << endl;
	}
//	cout << "                                      all=" << all << endl;
	return all;
}
int main() {
	int n, m, ans, sum = 0;
	cin >> n >> m;
	int g[n + 1][m + 1];
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			cin >> g[i][j];
			g[i][j] += g[i - 1][j];
		}
	for (int i = 1; i <= n; ++i) {
		for (int j = i; j <= n; ++j) {//列举所有j-i行的情况
			ans = 0;

			for (int k = 1; k <= m; ++k) {//{从第一列开始,
				//看后面每一列的列和是否为正,为正就加上,
				//同时把当前ans存储给sum,防止此刻已经是最大的ans了
				//不为正,我们就前功尽弃,从当前k继续往后找连续几列的列和都是整数的;}

				ans = ans + g[j][k] - g[i - 1][k];//此处:
				//是计算j-i这些行的列和
//				_____________________________
//				|  /                        |
//				|  /                        |
//				|  /                        |
//				|  /                        |
//				| i行      ____             |
//				|  / /     \  /             |
//				|  / /      \/              |
//				|  / /     j-i行            |
//				|  / /      /\              |
//				|  / /     /__\             |
//				| j行                       |
//				|                           |
//				|                           |
//				|___________________________|

				//加g[0]行->g[j]行的时候多加了一个g[0]行->g[i - 1]行
				//所以要减去
				if (ans > sum || sum == 0) {//把当前ans存储给sum
					sum = ans;
				}
				if (ans < 0) {
					ans = 0;
				}//这个代码是精髓,意思是:
				//从第一列开始往后推着走,
				//如果某一列的列和小于零,
				//那么这个和ans对于矩阵中最大和sum没有任何益处,
				//而且还把我们之前连续几列积累的ans给破坏掉了,
				//我们只有舍弃目前的ans,
				//从当前k继续往后看有没有连续的最大值;
				//同时我们需要在ans判断之前把当前ans存储起来,
				//防止万一这个ans已经是最大的了;
			}
		}
	}
	cout << sum;
	return 0;

}

如果使用四层嵌套则时间复杂度会达到O(n^3*m):

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
int cal(int a[][502], int f[], int s[]) {
//	cout << "从" << f[0] << "," << f[1] << "到" << s[0] << "," << s[1] << "————————————" << endl;
	int all = 0;
	for (int i = f[0]; i <= s[0]; i++) {
		for (int j = f[1] ; j <= s[1]; j++) {
//			cout << "a[" << i << "][" << j << "]==" << a[i][j] << endl;
			all = all + a[i][j];

		}
//		cout << endl;
	}
//	cout << "                                      all=" << all << endl;
	return all;
}
int main() {
	int n, m;
	cin >> n >> m;
	int a[n][502];
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> a[i][j];
		}
	}
//	for (int i = 0; i < n; i++) {
//		for (int j = 0; j < m; j++) {
//			cout << a[i][j] << " ";
//		}
//		cout << endl;
//	}
//	int f[2], s[2];
//	cout << "输入左上角坐标:";
//	cin >> f[0] >> f[1];
//	cout << "输入右下角坐标:";
//	cin >> s[0] >> s[1];
//	cout << "子矩阵的和为:\n" << cal(a, f, s) << endl;
	int all_all = 0;

//	cout << endl;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {

			for (int c = i ; c < n; c++) {
				for (int x = j; x < m; x++) {

					int f[2], s[2];
					f[0] = i;
					f[1] = j;
					s[0] = c;
					s[1] = x;
//					int sum = cal(a, f, s);
//					if (sum > all_all) {
//						all_all = sum;
//					}
					all_all = max(all_all, cal(a, f, s));
				}
			}

		}
	}
	cout << all_all;




	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值