SCU4400最大子阵题解(dp)

最大子阵

描述

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

Input

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

Output

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

Example Input

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

Example Output

10

样例说明

取最后一列,和为10。

数据规模和约定

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


对于这种题目一眼就看出来是最大子序列问题的变种,但是问题在于,这样的题目怎么做呢?两方向都dp吗?这样好像不太现实(反正我没成功),后来参考了网上的答案做出来了。
解法是采用通过遍历所有子矩阵的初末位置,来
控制列的范围(控制初末列位置),累加每列的行之和,采用dp的方法找到该子阵(该子阵由于控制列的范围,所以行的初末固定,列的大小与原数组一样)中行数均为起始到末尾的最大子子阵(即行数为起始到末尾的最大子阵),由于每种行数的情况下均会在遍历时处理,因此当遍历完全部的数组时,最大值即为矩阵的最大子矩阵。

本题的可行性在于,所有的初始,末尾列的位置都能在两个for循环遍历到,那么就可以得到每个确定初始,末尾列位置的小范围(设为range A)内每行的个数确定的(每行的个数为从初始到末尾列的位置的个数)最大矩阵A,由于任何一种初末列数确定的情况都能遍历到,所以会在某一时刻找到原来的小范围(range A)内每行的个数小于该确定范围的最大子矩阵B(该矩阵相当于初始,末尾列的位置为B矩阵的初始末尾列的位置每行的个数确定的最大矩阵A')所以能找到在确定了初末列的位置的子矩阵中最大的子矩阵,当范围为初始到末尾时,能找到这个数组的最大子阵,也就是正确答案。
AC代码:
#include <stdio.h>
#include <string.h>
int a[505][505], dp[505], row[505];
int main(void)
{
	int t, n, m;
	scanf("%d", &t);
	while(t--)
	{
		int ans = -2e9;
		scanf("%d %d", &n, &m);
		for(int i = 0; i < n; i++)
			for(int j = 0; j < m; j++)
				scanf("%d", &a[i][j]);
		for(int i = 0; i < m; i++)//确定初始列的位置 
		{
			memset(row, 0, sizeof(row));
			for(int p = i; p < m; p++)//确定末尾列的位置 
			{
				dp[0] = row[0] += a[0][p];//计算第0行确定的范围的数的累加(当范围扩大时只需要加上新范围,但起始列变动时得重置 
				if(dp[0] > ans)//如果第0行之和大于ans 
					ans = dp[0];
				for(int j = 1; j < n; j++)//采用dp方法找到行数确定的最大子矩阵 
				{
					row[j] += a[j][p];
					if(dp[j - 1] < 0)//小于0,加上下一行所有元素和肯定没有下一行所有元素和大 
						dp[j] = row[j];
					else
						dp[j] = dp[j - 1] + row[j]; 
					if(dp[j] > ans)
						ans = dp[j];
				}
			}
		} 
		printf("%d\n", ans);
	}							
	return 0;
}
			
 

  

 
 

  

猜你喜欢

转载自www.cnblogs.com/jacobfun/p/11267085.html