HDU——1599 最大子矩阵(暴力优化/动态规划矩阵前缀和)

原题链接: http://acm.hdu.edu.cn/showproblem.php?pid=1559

在这里插入图片描述
测试样例

Sample Input
1
4 5 2 2
3 361 649 676 588
992 762 156 993 169
662 34 638 89 543
525 165 254 809 280
Sample Output
2474

解题思路: 这道题非常好理解,操作也很好操作,暴力的话直接从左到右取矩阵,从上到下取矩阵,遍历所有子矩阵获取最大值即可。不过没这么简单,这自然会超时,不能解决此问题。这里介绍两种方法:一种是动态规划,一种是暴力解法的优化。

  • 暴力解法优化
    我们还是使用暴力解决,只不过需要进行优化处理,我们想想,我们从左到右平移矩阵的时候是不是添加了一列元素减少了一列元素,仅此而已。也就是说我们可能没必要这么计算。那么我们可以记录开始的那一列和结束的下一列。 对这两个进行处理即可。当然,这里要对 x x x行进行处理。(这种时间复杂度也是非常大的,大概700ms内可以AC。)

  • 动态规划(矩阵前缀)
    这里就非常巧妙了,我们发现,我们利用暴力解法还是会重复计算很多次没有必要的操作。那么我们尝试动态规划即可解决这个问题,这道题完全符合动态规划的使用条件。我们确定状态,如果我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 i i i j j j列的矩阵元素之和。那么状态转移方程自然可以列出来了(这里自行画图理解): d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − 1 ] [ j ] − d p [ i − 1 ] [ j − 1 ] + a dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+a dp[i][j]=dp[i][j1]+dp[i1][j]dp[i1][j1]+a a a a表示的就是第 i i i行第 j j j列的元素值。 这里减去 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1]是因为重复计算了这个 d p [ i − 1 ] [ j − 1 ] dp[i-1][j-1] dp[i1][j1]。知道这个,我们求出来了所有的 d p [ i ] [ j ] 。 dp[i][j]。 dp[i][j]那么我们怎么求子矩阵呢?子矩阵是不是可以用矩阵前缀来解决呢?我们如果设子矩阵的右下角坐标为 ( i , j ) (i,j) (i,j),其他三个角的坐标也自然清楚:左上 ( i − x , j − y ) (i-x,j-y) (ix,jy),左下 ( i , j − y ) (i,j-y) (i,jy),右下 ( i − x , y ) (i-x,y) (ix,y)。那么这个子矩阵的和是不是等于 d p [ i ] [ j ] − d p [ i ] [ j − y ] − d p [ i − x ] [ y ] + d p [ i − x ] [ j − y ] ( 因 为 左 上 角 前 缀 被 减 了 两 次 , 需 加 上 。 ) ( x ≤ i & & y ≤ j ) dp[i][j]-dp[i][j-y]-dp[i-x][y]+dp[i-x][j-y](因为左上角前缀被减了两次,需加上。)(x\leq i \&\&y\leq j) dp[i][j]dp[i][jy]dp[ix][y]+dp[ix][jy](xi&&yj)。OK,具体见代码。(这种方法比暴力快了大概500ms,推荐这种。)

暴力解法优化AC代码

/*
*邮箱:unique_powerhouse@qq.com
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e3+2;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

int t;
int n,m;
int x,y;//我们要找到的子矩阵。
int nums[maxn][maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
		while(t--){
			cin>>n>>m;
			cin>>x>>y;
			rep(i,1,n){
				rep(j,1,m){
					cin>>nums[i][j];
				}
			}
			int cnt1=1,cnt2=1;
			ll ans=0,result=0;
			int temp1,i,j;
			while(cnt1+x-1<=n){
				cnt2=1;
				ans=0;
				for(i=cnt1;i<=cnt1+x-1;i++){
					for(j=cnt2;j<=cnt2+y-1;j++){
						ans+=nums[i][j];
					}
				}
				result=max(ans,result);
				temp1=j;
				while(temp1<=m){
					for(int k=cnt1;k<=cnt1+x-1;k++){
						ans=ans+nums[k][temp1]-nums[k][cnt2];
					}
					result=max(ans,result);
					temp1++;cnt2++;
				}
				cnt1++;
			}
			cout<<result<<endl;
		}
	}
	return 0;
}

动态规划(矩阵前缀)AC代码

/*
*邮箱:unique_powerhouse@qq.com
*blog:https://me.csdn.net/hzf0701
*注:文章若有任何问题请私信我或评论区留言,谢谢支持。
*
*/
#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e3;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//


int t;
int n,m,x,y;
int dp[maxn][maxn];
int main(){
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
		while(t--){
			cin>>n>>m;
			cin>>x>>y;
			int temp;
			memset(dp,0,sizeof(dp));
			int result=0;
			rep(i,1,n){
				rep(j,1,m){
					cin>>temp;
					dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+temp;
					if(i>=x&&j>=y){
						result=max(result,dp[i][j]-dp[i-x][j]-dp[i][j-y]+dp[i-x][j-y]);
					}
				}
			}
			cout<<result<<endl;
		}
	}
	return 0;
}

posted @   unique_pursuit  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示