链接: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;
}