DP动态规划--最大子矩阵和问题(最大子段问题的推广即二维最大子段和问题)-To the Max(POJ - 1050题解)

DP动态规划–最大子矩阵和问题(最大子段和问题的推广,即二维最大子段和问题 )-To the Max

POJ - 1050原题点这里

(温馨提示:本题是一维最大子段和问题的推广,建议先了解一下一维子段和问题:DP动态规划–最大子段和问题
要是大佬就无所谓了,直接往下看:)

题目

Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:
9 2
-4 1
-1 8
and has a sum of 15.

INPUT:
The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N^2 integers separated by whitespace (spaces and newlines). These are the N^2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].

OUTPUT:
Output the sum of the maximal sub-rectangle.

Sample Input
4
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2

Sample Output
15

题意
题目看着很长,但其实意思很简单,给一个N*N的矩阵,找一个该矩阵的子矩阵,使这个子矩阵中数值之和最小。

题目解析
首先多维纯暴力遍历O(n^4)显然是不行的。那我们就在暴力的基础上尽量优化:

  • 算法思想:
  1. 首先我们可以把原始矩阵的最大子矩阵理解为矩阵第1行到第n行之间的最大子矩阵,那么肯定存在i与j(1<=i<=j<=n),使这个最大子矩阵横跨第i行到第j行之间。
  2. 那我们就遍历i、j找每一对i、j之中最大的那个子矩阵称为局部最大子矩阵,然后再在这些局部最大子矩阵中找出最大的矩阵
  • 具体步骤:
  1. 设s(x1,x2,y1,y2)表示从点(x1,y1)到点(x2,y2)之间子矩阵的值。那么我们就遍历x1,x2(即第i行到第j行,作i与j的嵌套循环),求出s(i,j,y1,y2)中的最大值(1<=i<=j<=n)
    那当我们遍历到一个i、j时,我们要怎么去求这个s(i,j,y1,y2)呢?我们接下着往下看:)
  2. 对于确定的i,j,要去求s(i,j,y1,y2)的最大值,我们只需要根据y1,y2来作判断:
    如图,此时i=2,j=4,我们要求横跨第2行到第4行之间的最大子矩阵,那我们再去把每一列都看成一个具体的值dp1,dp2,dp3,dp4。那我们就把问题化成了一维数组{4,11,-10,1}的最大子段问题。我们便轻松的获得了s(2,4,y1,y2)的最大值。
    (一维最大子段问题比较简单,可以直接看下面源码里的MaxSum_list函数或者到再回这里看看DP动态规划–最大子段和问题)。

在这里插入图片描述

  1. 我们得到了每一对i,j的最大s(i,j,y1,y2),我们只要求出所有i、j其中最大的s值,事实上可以在循环过程中用一个Max变量去维护最大值,最后直接输出就行了。

参照代码与注释:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<ctime>
using namespace std;
const int inf = 0x3f3f3f3f;
clock_t START, END;
int MaxSum_list(int n, int a[110]) {//求出一维数组(a[])的最大子段值
	int Max = -inf;
	//注意这里要用-inf初始化,否则遇到全负的情况则会出现最大值为0的情况
	//除非规定全负时最大子段值就是0
	int dp = 0;
	for (int i = 1; i <= n; i++) {
		dp = max(dp + a[i], a[i]);
		//如果dp[i-1]<0,则dp[i]=a[i];否则dp[i]=dp[i-1]+a[i]
		//由于我们只需要知道最大值,所以不需要开dp数组,直接用Max维护最大值就行了
		Max = max(dp, Max);
	}
	return Max;
}
int MaxSum_matrix(int m, int n, int a[110][110]) {//求出多维数组(矩阵a[][])的最大子矩阵和
	int Max = -inf;
	int dp[110];//dp[k]表示第k列所求行之间(i--j行之间)的数值之和
	for (int i = 1; i <= m; i++) {
		memset(dp, 0, sizeof(dp));//初始化b数组为0
		
		for (int j = i; j <= n; j++) {//循环j 得到i--j行之间的数据
			for (int k = 1; k <= n; k++) {
				dp[k] += a[j][k];//更新此时i--j行之间的b值
			}
			Max = max(MaxSum_list(n, dp), Max);//此时的MaxSum_list(n, dp)指在i--j行之间的最大子矩阵
		}
	}
	return Max;
}
int main()
{
	int N;
	int a[110][110];
	memset(a, 0, sizeof(a));
	cin >> N;
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++) {
			cin >> a[i][j];
		}
	}
	START = clock();
	cout << MaxSum_matrix(N, N, a) << endl;
	END = clock();
	double endtime = (double)(END - START) / CLOCKS_PER_SEC;
	cout << "Total time:" << endtime * 1000 << "ms" << endl;
	return 0;
}
  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值