题目链接
https://vjudge.net/contest/398864#problem/C
解题思路
代码1:
dp[i][j]表示以(1,1)为左上角,以(i,j)为右下角的矩阵的和;
枚举矩阵的左边界和右边界,再利用尺取法选取上边界和下边界,判断选取的子矩阵的和是否小于等于k。
这个好理解。
代码2:
大致思路一样,枚举左边界和右边界,尺取上边界和下边界。
不同之处在代码中注释。
AC代码1:
#include<bits/stdc++.h>
#define ll long long
#define sc(x) scanf("%lld",&x)
#define pr(x) printf("%lld\n",x)
using namespace std;
const int N=300;
ll ans,n,m,K,dp[N][N];
int main(){
sc(n);sc(m);sc(K);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
sc(dp[i][j]);
dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-1]+dp[i][j];
}
for(int wL=1;wL<=m;wL++)
for(int wR=wL;wR<=m;wR++){
ll p1=1,p2=1;
while(p1<=n && p2<=n){
if(dp[p2][wR]-dp[p2][wL-1]-dp[p1-1][wR]+dp[p1-1][wL-1]<=K) ans=max(ans,(wR-wL+1)*(p2-p1+1)),p2++;
else p1++,p2=p1;
}
}
if(ans==0) cout<<-1<<endl;
else pr(ans);
}AC代码2:
#include<bits/stdc++.h>
#define ll long long
#define sc(x) scanf("%lld",&x)
#define pr(x) printf("%lld\n",x)
using namespace std;
const int N=300;
ll n,m,K,ans;
ll dp[N],sum[N][N];//两个表示前缀和数组,sum[i][j]表示第i行,前j列的和,dp[i]表示前i行,从第L列到第R列的和//配合下面代码会好理解点
ll h[N],mp[N][N];//h[i]表示第i行,从第L列到第R列的和,mp单纯存的矩阵
int main(){
sc(n);sc(m);sc(K);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
sc(mp[i][j]);
sum[i][j]=sum[i][j-1]+mp[i][j];//每一行的前缀和存在sum数组中
}
}
for(int i=1;i<=m;i++)//枚举左边界
for(int j=i;j<=m;j++){//枚举右边界
for(int k=1;k<=n;k++) h[k]=sum[k][j]-sum[k][i-1],dp[k]=dp[k-1]+h[k];//先算出从左边界到右边界范围内每一行对应的和,保存于h中;左边界到右边界范围内的前i行的子矩阵和保存于dp中
ll p1=1,p2=1;
while(p1<=n && p2<=n){//尺取,这个条件很关键,容易漏p2<=n
if(dp[p2]-dp[p1-1]<=K) ans=max(ans,(j-i+1)*(p2-p1+1)),p2++;
else p1++,p2=p1;
}
}
if(ans==0) cout<<-1<<endl;
else pr(ans);
} 
京公网安备 11010502036488号