题目链接:点击打开链接
题意:第一行给出 r,c,x,y,z 五个字母,再给出一个 r行c列的整数矩阵,求一个行数不超过x,列数不超过y,并且0的数量不超过z的子矩阵,使得其中的元素之和最大,输出这个和。
我们先n方暴力for出这个子矩阵 列的范围,同时控制列数不超过y,不然就break。
然后我们for 子矩阵的行,从 1 到 r。这时候,如果我们之前把各行的元素和以及0的数量和,用前缀和统计好,会发现一行就相当于一个元素,现在就是在1~r个元素中,找到一个连续的范围,使得他们的和最大,同时满足题目中的条件。
这时候就可以用单调队列,维护这r的元素的新的前缀和 从小到大。队首的元素如果不满足了,就出队。用数组模拟即可。
#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
ll m[600][600];
ll pre[600][600];
ll pre0[600][600];
ll s[600],t[600],t0[600];
int main(void)
{
ll r,c,x,y,z;
while(~scanf("%lld%lld%lld%lld%lld",&r,&c,&x,&y,&z)){
for(int i=1;i<=r;i++){
for(int j=1;j<=c;j++){
scanf("%lld",&m[i][j]);
pre[i][j]=pre[i][j-1]+m[i][j];
pre0[i][j]=pre0[i][j-1]+(m[i][j]==0);
}
}
ll ans=0;
for(int i=1;i<=c;i++){
for(int j=i;j<=c;j++){
if(j-i+1>y) break;
ll L=0,R=1;
ll num0=0,tot=0;
s[0]=0;
for(int k=1;k<=r;k++){
num0+=pre0[k][j]-pre0[k][i-1];
tot+=pre[k][j]-pre[k][i-1];
t0[k]=num0;
t[k]=tot;
while(L<R&&t[s[R-1]]>tot)R--;
s[R++]=k;
while(L<R&&(k-s[L]>x||num0-t0[s[L]]>z)){
L++;
}
ans=max(ans,tot-t[s[L]]);
}
}
}
printf("%lld\n",ans);
}
return 0;
}