原题链接: 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
解题思路: 这道题非常好理解,操作也很好操作,暴力的话直接从左到右取矩阵,从上到下取矩阵,遍历所有子矩阵获取最大值即可。不过没这么简单,这自然会超时,不能解决此问题。这里介绍两种方法:一种是动态规划,一种是暴力解法的优化。
- 暴力解法优化
我们还是使用暴力解决,只不过需要进行优化处理,我们想想,我们从左到右平移矩阵的时候是不是添加了一列元素减少了一列元素,仅此而已。也就是说我们可能没必要这么计算。那么我们可以记录开始的那一列和结束的下一列。 对这两个进行处理即可。当然,这里要对行进行处理。(这种时间复杂度也是非常大的,大概700ms内可以AC。)
- 动态规划(矩阵前缀)
这里就非常巧妙了,我们发现,我们利用暴力解法还是会重复计算很多次没有必要的操作。那么我们尝试动态规划即可解决这个问题,这道题完全符合动态规划的使用条件。我们确定状态,如果我们用表示
行
列的矩阵元素之和。那么状态转移方程自然可以列出来了(这里自行画图理解):
。
表示的就是第
行第
列的元素值。 这里减去
是因为重复计算了这个
。知道这个,我们求出来了所有的
那么我们怎么求子矩阵呢?子矩阵是不是可以用矩阵前缀来解决呢?我们如果设子矩阵的右下角坐标为
,其他三个角的坐标也自然清楚:左上
,左下
,右下
。那么这个子矩阵的和是不是等于
。OK,具体见代码。(这种方法比暴力快了大概500ms,推荐这种。)
暴力解法优化AC代码
/*
*
*/
//POJ不支持
//i为循环变量,a为初始值,n为界限值,递增
//i为循环变量, a为初始值,n为界限值,递减。
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代码
/*
*
*
*/
//POJ不支持
//i为循环变量,a为初始值,n为界限值,递增
//i为循环变量, a为初始值,n为界限值,递减。
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;
}