题目链接:https://vjudge.net/problem/HihoCoder-1502
最大子矩阵
给定一个NxM的矩阵A和一个整数K,小Hi希望你能求出其中最大(元素数目最多)的子矩阵,并且该子矩阵中所有元素的和不超过K。
Input
第一行包含三个整数N、M和K。
以下N行每行包含M个整数,表示A。
对于40%的数据,1 <= N, M <= 10
对于100%的数据,1 <= N, M <= 250 1 <= K <= 2147483647 1 <= Aij <= 10000
Output
满足条件最大的子矩阵所包含的元素数目。如果没有子矩阵满足条件,输出-1。
Sample Input
3 3 9 1 2 3 2 3 4 3 4 5
Sample Output
4
思路:先求出矩阵的二维前缀和,用二维数组 a 进行存储。
然后枚举两列,对行采用尺取法进行做。具体看代码。
注:要是枚举列,再枚举行就会超时。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll n,m,k,ans;
ll a[255][255];
ll cal(ll x,ll y,ll j)
{
return a[j][y]-a[j][x-1]-a[j-1][y]+a[j-1][x-1];
}
void solve(ll x,ll y)
{
ll j,l;
ll sum=0;
for(j=0,l=1;j<n;)//对行进行尺取法处理
{
while(j<n&&sum<=k){
ans=max(ans,(j-l+1)*(y-x+1));
j++;
sum+=cal(x,y,j);//一行行加
}
if(sum<=k)ans=max(ans,(j-l+1)*(y-x+1));
while(l<=j&&sum>k)
{
sum-=cal(x,y,l);//一行行减
l++;
}
}
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
ll i,j,l;
memset(a,0,sizeof(a));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%lld",&l);
a[i][j]=a[i][j-1]+a[i-1][j]-a[i-1][j-1]+l;//求前缀和
}
}
ans=0;
for(i=1;i<=m;i++)//枚举列
{
for(j=i;j<=m;j++)
{
solve(i,j);
}
}
if(ans){
printf("%lld",ans);
}
else printf("-1");
return 0;
}
超时代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll n,m,k,ans;
ll a[255][255];
ll cal(ll i,ll j,ll x,ll y)
{
return a[j][y]-a[j][x-1]-a[i-1][y]+a[i-1][x-1];
}
void solve(ll x,ll y)
{
ll i,j,l;
ll sum=0;
for(i=1;i<=n;i++)//枚举行
{
for(j=i;j<=n;j++){
sum=cal(i,j,x,y);
if(sum<=k)ans=max(ans,(y-x+1)*(j-i+1));
}
}
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
ll i,j,l;
memset(a,0,sizeof(a));
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%lld",&l);
a[i][j]=a[i][j-1]+a[i-1][j]-a[i-1][j-1]+l;//求前缀和
}
}
ans=0;
for(i=1;i<=m;i++)//枚举列
{
for(j=i;j<=m;j++)
{
solve(i,j);
}
}
if(ans){
printf("%lld",ans);
}
else printf("-1");
return 0;
}