Wannafly挑战赛19 B单调队列

链接:https://www.nowcoder.com/acm/contest/131/B
来源:牛客网
 

时间限制:C/C++ 4秒,其他语言8秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

矩阵 M 包含 R 行 C 列,第 i 行第 j 列的值为 Mi,j。
请寻找一个子矩阵,使得这个子矩阵的和最大,且满足以下三个条件:
子矩阵的行数不能超过 X 行。
子矩阵的列数不能超过 Y 列。
子矩阵中 0 的个数不能超过 Z 个。
请输出满足以上条件的最大子矩阵和。

输入描述:

第一行输入五个整数 R,C,X,Y,Z。

接下来 N 行,每行输入 M 个整数,第 i 行第 j 列的整数表示 Mi,j。

1 ≤ R,C ≤ 500.

1 ≤ X ≤ R.
1 ≤ Y ≤ C.
1 ≤ Z ≤ R x C.
-109 ≤ Mi,j  ≤ 109

输出描述:

输出满足以上条件的最大子矩阵和。

 

示例1

输入

复制

5 5 3 3 4
0 0 10 0 0
3 4 0 2 3
-1 3 0 -8 3
0 0 32 -9 3
3 0 45 3 0

输出

复制

82

示例2

输入

复制

2 2 2 2 2
-1 -1
-1 -1

输出

复制

0

题解:

先预处理每行每列。维护一个单调增队列, 进行答案更新,。

#include <bits/stdc++.h>

using namespace std;
const int maxn = 505;
using ll = long long ;

int main()
{

    ios::sync_with_stdio(false), cin.tie(0);
    int n, m, x, y, z;
    cin >> n >> m >> x >> y >> z;
    vector<vector<ll> > e(n + 1, vector<ll>(m + 1, 0) );
    vector<vector<ll> > line_sum = e;
    vector<vector<ll> > line_0 = e;
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= m; j ++) {
            cin >> e[i][j];
            line_sum[i][j] = line_sum[i - 1][j] + e[i][j];
            line_0[i][j] = line_0[i - 1][j] + (e[i][j] == 0);
        }
    }
//    debug(line_sum);
//    debug(line_0);
    ll res = 0;
    for(int up = 1; up <= n; up ++) {
        for(int down = up; down <= n; down ++) {
            if(down - up + 1 > x) break;
            vector<ll> cnt0 (m + 1, 0);
            vector<ll> sum (m + 1, 0);
            for(int j = 1; j <= m; j ++) {
                cnt0[j] = line_0[down][j] - line_0[up - 1][j];
                sum[j] = line_sum[down][j] - line_sum[up - 1][j];
                cnt0[j] += cnt0[j - 1];
                sum[j] += sum[j - 1];
            }
            deque<int> deq = {0};
            for(int j = 1; j <= m; j ++) {
                while(!deq.empty() && sum[deq.back()] >= sum[j]) deq.pop_back(); //大于当前元素sum[j], pop
                deq.push_back(j);
                while(!deq.empty() && deq.front() + y < j) deq.pop_front(); //距离超出y
                while(!deq.empty() && cnt0[j] - cnt0[deq.front()] > z) deq.pop_front(); //0的个数超出z个
                if(!deq.empty()) res = max(res, sum[j] - sum[deq.front()]); //更新答案
            }
        }
    }
    cout << res << '\n';

    return 0;
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值