Sub Matrix Sum
You have written many programs to search mazes so matrix search shouldn’t be any different, or will it?
The Problem:
An integer matrix with R rows and C columns has 图片1.png sub matrices. We want to select a sub matrix with sum (the sum of all integers in it) greater than or equal to a given integer S. We want the size of the sub matrix to be the least possible. The size of a sub matrix is defined as the number of elements in that sub matrix (i.e., number of rows * number of columns in that sub matrix).
The Input:
The first input line consists of three integers R, C (1 ≤ R ≤ 100,000; 1 ≤ C ≤ 100,000;1 ≤ R*C ≤ 100,000) and S. Next R lines contain the description of the matrix. Each of these R lines contains C integers separated by a single space. All integers (other than R and C) are between -10^9−10
9
and +10^910
9
, inclusive.
The Output:
Print the size of the minimum sub matrix whose sum is greater or equal to the given S. If there is no such sub matrix, output -1.
样例输入1复制
3 3 26
1 2 3
4 5 6
7 8 9
样例输出1复制
4
样例输入2复制
3 3 0
-1 -2 -3
-4 -5 -6
-7 -8 -9
样例输出2复制
-1
样例输入3复制
2 2 1
-1 -2
0 2
样例输出3复制
1
题目大意:
给出一个矩阵,找出一个子矩阵让他的和 大于等于S,并且使得子矩阵尽量小,首先看数据范围就很怪,R*C<=100000,开数组直接爆炸 ,只能用vector
题目分析:
枚举矩阵的两个角,也就是O(N^6)暴力,肯定都会,加个二位前缀和,时间复杂度降两维,正确的解法是:
- 处理一个已有 列和 的二位前缀和
- 然后枚举两行,(起始行 和 终止行)sum[k] = v[j][k] - v[i -1] sum[k] += sum[k - 1]这两句 把枚举的两行之间的数据按照 列 压缩到了一行里
- 接下来就是在这一行里,找一段连续序列的和 使得该和大于等于S 并且尽量小
- 滑动窗口可做也很好理解,但是我竟然忘了 滑动窗口不能有负数 只能上单调栈 + 二分答案 关于单调栈的研究不再赘述
- 具体实现看代码
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <map>
// 这个题本来我想用 滑动窗口来检验 但是滑动窗口并不适用于有负数的情况, 比如样例3就过不去
// 还是去研究 单调栈
using namespace std;
#define Maxn 100005
const int INF = 0x3f3f3f3f;
#define LL long long
vector<vector<LL> > v;
vector<LL> st;// 单调栈
LL sum[Maxn],s;
int r,c;
int Solve() {// c lie
int res = INF;
st.clear(); st.push_back(0);
for(int i=1; i<=c; i++) {
int l = 0,r = st.size() - 1;
LL tmp = sum[i] - s;
int Ans = -1;
while(l <= r) {
int Mid = l + r >> 1;
if(sum[st[Mid]] <= tmp) {
l = Mid + 1;
Ans = st[Mid];
}
else r = Mid - 1;
}
if(Ans != -1) res = min(res,i - Ans);
while(!st.empty() && sum[st[st.size() - 1]] >= sum[i]) st.pop_back();
st.push_back(i);
}
if(res == INF) res = -1;
return res;
}
int main(int argc,char* argv[]) {
int T ; scanf("%d",&T);
while(T--) {
scanf("%d %d %lld",&r,&c,&s);
v.clear();
if(r < c) {
v.resize(r + 1);
for(int i=0; i<=r; i++) v[i].resize(c + 1);
for(int i=1; i<=r; i++)
for(int j=1; j<=c; j++) scanf("%lld",&v[i][j]);
} else {
v.resize(c + 1);
for(int i=0; i<=c; i++) v[i].resize(r + 1);
for(int i=1; i<=r; i++)
for(int j=1; j<=c; j++) scanf("%lld",&v[j][i]);
swap(c,r);
}
// 现在一定是r 行 c列 并且r《c
for(int i=0; i<=c; i++) v[0][i] = 0;
for(int i=1; i<=r; i++)
for(int j=1; j<=c; j++)
v[i][j] = v[i - 1][j] + v[i][j];
int Ans = r * c + 1;
for(int i=1; i<=r; i++)
for(int j=i; j<=r; j++) {// 枚举两行之间 就是 i、j两行之间
sum[0] = 0;
for(int k=1; k<=c; k++) sum[k] = v[j][k] - v[i -1][k];// i j两行之间的 列和
for(int k=1; k<=c; k++) sum[k] += sum[k - 1];
int ret = Solve();
if(ret == -1) continue;
else Ans = min(Ans,ret * (j - i + 1));
}
if(Ans == r * c + 1) printf("-1\n");
else printf("%d\n",Ans);
}
return 0;
}