最大子阵
蓝桥杯
问题描述
给定一个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。
做法:
1.做二维最大子阵前,先了解一维最大子阵
- 它又被叫做
一维最大连续子序列和
前 3 个 数 的 最 大 之 和 = m a x { 第 3 个 数 的 值 ( 此 另 起 一 个 线 程 后 , 下 一 个 即 从 ? 开 始 ) 前 2 个 数 的 最 大 之 和 + 第 3 个 数 的 值 前3个数的最大之和=max \begin{cases} 第3个数的值(此另起一个线程后,下一个即从?开始) \\ 前2个数的最大之和+第3个数的值 \end{cases} 前3个数的最大之和=max{第3个数的值(此另起一个线程后,下一个即从?开始)前2个数的最大之和+第3个数的值
第 ? 个 数 到 3 的 最 大 之 和 = m a x { 第 3 个 数 的 值 第 ? 个 数 到 2 的 最 大 之 和 + 第 3 个 数 的 值 第?个数到3的最大之和=max \begin{cases} 第3个数的值 \\ 第?个数到2的最大之和+第3个数的值 \end{cases} 第?个数到3的最大之和=max{第3个数的值第?个数到2的最大之和+第3个数的值
d p [ i ] = m a x { d a t a [ i ] d p [ i − 1 ] + d a t a [ i ] dp[i]=max \begin{cases} data[i] \\ dp[i-1]+data[i] \end{cases} dp[i]=max{data[i]dp[i−1]+data[i]
- 以上,便得到了 1维最大子阵 的状态转移方程。
2.知道了1维最大子阵后,再说说二维最大子阵
写在前面: 直接在 一维子阵的状态转移方程 的基础上,再推一个二维子阵(类似于dp[i][j])的状态转移方程是比较困难的。所以下面在另一个方法中 利用一维子阵的状态转移方程,而不是直接推导。
下面借鉴一下别人的内容。
3.代码
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cmath>
using namespace std;
int dp[600][600]={0};//给后面 最大子序列和 进行dp用的
int sum[600][600]={0};//sum[3][1]就代表 1列的 一二三行之和
int main()
{
//输入矩阵,并准备好叠加起来的数组sum
int n,m;cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int data;cin>>data;//输入
sum[i][j]=data+sum[i-1][j];//叠加前一行
}
}
//=====================核心部分========================
int ans=-6000;//记录最大值
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){//遍历相减的两行
//处理 行数确定时,不同列之间的 最大子序列和(一维)
int dp2[600]={0};
for(int lie=1;lie<=m;lie++){//控制列数
//最大子序列和的 动态规划
//(这里不要独立成函数 因为会大大增加耗时
int data=sum[j][lie]-sum[i-1][lie];
dp2[lie]=max(data,dp2[lie-1]+data);
if(dp2[lie]>ans)ans=dp2[lie];
}
}
}
//====================================================
cout<<ans<<endl; return 0;
}