UCF Local Programming Contest 2019(Practice) F Sub Matrix Sum 【降维+二分单调栈】

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 图片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). 

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

样例输入2

3 3 0
-1 -2 -3
-4 -5 -6
-7 -8 -9 

样例输出2

-1 

样例输入3

2 2 1
-1 -2
0 2 

样例输出3

 题目大意:

给出一个大小为 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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 PII;
typedef pair PLL;
typedef vector VI;
const ll mod = 1e9 + 7;
const int maxn = 100000 + 5;

vector> mat;
ll num[maxn];
int r,c;
ll s;
vector 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;
}


 

你可能感兴趣的:(ACM学习,数据结构)