Description
You have written many programs to search mazes so matrix search shouldn't be any different, or will it?
Problem
An integer matrix with R rows and C columns has
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).
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 and +10^9, inclusive.
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
题目大意:
给出一个大小为 R×C 的矩阵,要求选出一个子矩阵,在子矩阵和不小于 S 的条件下,输出子矩阵的最小大小。
分析:
涉及到二维子矩阵的元素和,先降维。枚举起始行 i 和结束行 j ,将 i 到 j 之间的行按列压缩到一维,则对于这个一维数组,问题转化为找到一对 l 和 r ,使得 r-l+1 最小,并且 l 到 r 之间的元素和大于等于 S 。
对一维数组求前缀和,由于这里有负数元素,前缀和会减小,所以用单调栈记录递增的前缀和,对于每个位置 k ,二分单调栈找到第一个小于等于 num[k]-S 的前缀和 num[x] ,则 x 到 k 之间的元素和满足大于等于 S ,并且 x 是满足该条件的最大下标。
具体解释见代码。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
#define INF 0x3f3f3f3f
#define mst(a,num) memset(a,num,sizeof a)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef vector<int> VI;
const ll mod = 1e9 + 7;
const int maxn = 100000 + 5;
vector<vector<ll>> mat;
ll num[maxn];
int r,c;
ll s;
vector<ll> sta; //单调栈
int solve(){
int res=INF;
sta.clear();
sta.push_back(0);
rep(i,1,c){
int l=0,r=sta.size()-1;
ll tmp=num[i]-s;
int ans=-1;
while(l<=r){ //二分查找
int mid=(l+r)/2;
if(num[sta[mid]]<=tmp){
l=mid+1;
ans=sta[mid];
}
else{
r=mid-1;
}
}
if(ans!=-1){
res=min(res,i-ans);
}
while(!sta.empty()&&num[sta[sta.size()-1]]>=num[i]){ //单调栈操作
sta.pop_back();
}
sta.push_back(i);
}
return res;
}
int main() {
scanf("%d%d%lld",&r,&c,&s);
//选择r和c中较小的作为行进行存储
if(r < c){
mat.resize(r+1);
rep(i,0,r){
mat[i].resize(c+1);
}
rep(i,1,r){
rep(j,1,c){
scanf("%lld",&mat[i][j]);
}
}
}
else{
mat.resize(c+1);
rep(i,0,c){
mat[i].resize(r+1);
}
rep(i,1,r){
rep(j,1,c){
scanf("%lld",&mat[j][i]);
}
}
swap(r,c);
}
rep(i,1,c){
mat[0][i]=0;
}
//预处理二维前缀和
rep(i,1,r){
rep(j,1,c){
mat[i][j]=mat[i-1][j]+mat[i][j];
}
}
int ans=INF;
rep(l,1,r){
rep(rr,l,r){
num[0]=0;
rep(i,1,c){
num[i]=mat[rr][i]-mat[l-1][i];
}
rep(i,1,c){
num[i]+=num[i-1];
}
int ret=solve();
if(ret==INF) continue;
ans=min(ans,(rr-l+1)*ret);
}
}
printf("%d\n",ans==INF?-1:ans);
return 0;
}