【牛客网】【Wannafly挑战赛19】B-矩阵 【单调队列】

题目链接:点击打开链接

题意:第一行给出 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;
}

猜你喜欢

转载自blog.csdn.net/gymgym1212/article/details/80971882