UCF Local Programming Contest 2019(Practice) Sub Matrix Sum

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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值