题目链接:
#1502 : 最大子矩阵
-
3 3 9 1 2 3 2 3 4 3 4 5
样例输出
-
4
描述
给定一个NxM的矩阵A和一个整数K,小Hi希望你能求出其中最大(元素数目最多)的子矩阵,并且该子矩阵中所有元素的和不超过K。
输入
第一行包含三个整数N、M和K。
以下N行每行包含M个整数,表示A。
对于40%的数据,1 <= N, M <= 10
对于100%的数据,1 <= N, M <= 250 1 <= K <= 2147483647 1 <= Aij <= 10000
输出
满足条件最大的子矩阵所包含的元素数目。如果没有子矩阵满足条件,输出-1。
这类矩阵题目,第一反应就是首先需要在O(mn)时间将矩阵内每个位置到左上角所构成的子矩阵包含的元素和求出来,一次从左上向右下求解,每次运算利用之前的信息来计算: a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + a[i][j]; 做完这个处理之后,任意的子矩阵均可通过下边的公式在O(1)时间求出:
第i行到第j行、第p列到第q列构成的子矩阵则可由上一步预处理过的矩阵和 sum=a[j][q] + a[i-1][p-1] - a[j][p-1] - a[i-1][q];
首先考虑的是二分枚举可能的答案,但是对于一个可能的答案,有可能有不同的行列组合,比如包含18个元素的矩阵可能是2行9列,也可能是3行6列,处理起来比较麻烦(画蛇添足),所以放弃了。
要求解满足条件的子矩阵,枚举行和列即可。注意枚举列时可先固定矩形右边的列,依次从左向右枚举左边的列,如果满足条件(即sum<k),即可停止枚举左边的列,因为从左到右枚举,所以包含的元素是越来越少的。参考代码如下:
#include <iostream>
#include <algorithm>
using namespace std;
int n, m, k;
int ans = 0;
int a[255][255] = {0};
int main()
{
// freopen("in.txt", "r", stdin);
cin>>n>>m>>k;
for(int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
cin>>a[i][j];
//第一行
for(int i=2; i<=n; i++)
a[1][i] = a[1][i-1] + a[1][i];
//第一列
for(int j=2; j<=m; j++)
a[j][1] = a[j-1][1] + a[j][1];
for(int i=2; i<=n; i++)
for(int j=2; j<=m; j++)
a[i][j] = a[i-1][j] + a[i][j-1] - a[i-1][j-1] + a[i][j];
二分枚举
//int l = 0, r = m*n, mid;
//while(l < r)
//{
// mid = l + (r - l) >> 1
// if(check(mid))
// {
// ans = mid;
// l = mid +1;
// }
// else
// r = m - 1;
//}
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++) //枚举行
{
for(int q=1; q<=m; q++)
for(int p=1; p<=q; p++)
{
int sum = a[j][q] + a[i-1][p-1] - a[j][p-1] - a[i-1][q]; //矩阵 i, j, p, q
if (sum <= k)
{
ans = max(ans, (j-i+1)*(q-p+1));
break;
}
}
}
cout<<ans;
return 0;
}