【蓝桥备赛】统计子矩阵

题目链接

统计子矩阵

个人思路分析

当看到统计有多少个子矩阵所有数的和不超过指定的整数K,直接就想到了二维前缀和。
对于前缀和知识点还不清楚的,可以去浏览下我之前关于前缀和的基础介绍【洛谷】前缀和入门。后面如果这个链接因为我修改了前面博客内容失效了,大家也可以去我主页里->算法做题笔记专栏->洛谷专栏下面去查找这篇博客。
想要求子矩阵的和,那么我们就先对输入的 n*m的矩阵进行一个预处理,获取该矩阵的前缀和。
当然这题的关键不是去获取前缀和,而是如何去遍历所有满足条件的值。

示例代码

Java版

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class Main {
    static long[][] arr;
    static int n, m;
    static long k;

    // Java 的快读
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    static StreamTokenizer st = new StreamTokenizer(br);
    static int nextInt() throws IOException {
        st.nextToken();
        return (int) st.nval;
    }
    static long nextLong() throws IOException {
        st.nextToken();
        return (long) st.nval;
    }

    public static void main(String[] args) throws IOException {
        // 数据读入
        n = nextInt();
        m = nextInt();
        k = nextLong();
        arr = new long[n + 1][m + 1];
        // 预处理前缀和
        for(int i = 1; i <= n; ++i) {
            for(int j = 1; j <= m; ++j) {
                long t = nextLong();
                arr[i][j] = arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1] + t;
            }
        }
        long res = 0;
        // 遍历行范围:从 x1 到 x2
        for(int x1 = 1; x1 <= n; ++x1) {
            for(int x2 = x1; x2 <= n; ++x2) {
                // 遍历列范围:从 y1 到 y2
                // 当前情况满足条件时,只移动 y2,不移动 y1
                for(int y1 = 1, y2 = 1; y2 <= m; ++y2) {
                    // 当不满足条件时,移动 y1
                    while (y1 <= y2 && !check(x1, y1, x2, y2)) {
                        ++y1;
                    }
                    // 在满足条件的情况下,从 y1 到 y2 区域都是满足的,就不重复计算了
                    res += (y2 - y1 + 1);
                }
            }
        }
        System.out.println(res);
    }

    // 判断当前是否满足条件
    private static boolean check(int x1, int y1, int x2, int y2) {
        long sum = arr[x2][y2] - arr[x1 - 1][y2] - arr[x2][y1 - 1] + arr[x1 - 1][y1 - 1];
        return sum <= k;
    }
}

C/C++版

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 503;

int n, m;
ll k, arr[N][N] = {0}, t;

static int check(int x1, int y1, int x2, int y2)
{
    long sum = arr[x2][y2] - arr[x1 - 1][y2] - arr[x2][y1 - 1] + arr[x1 - 1][y1 - 1];
    return sum <= k;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> m >> k;
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
        {
            cin >> t;
            arr[i][j] = t + arr[i - 1][j] + arr[i][j - 1] - arr[i - 1][j - 1];
        }
    ll res = 0;
    for (int x1 = 1; x1 <= n; ++x1)
    {
        for (int x2 = x1; x2 <= n; ++x2)
        {
            // 遍历列范围:从 y1 到 y2
            // 当前情况满足条件时,只移动 y2,不移动 y1
            for (int y1 = 1, y2 = 1; y2 <= m; ++y2)
            {
                // 当不满足条件时,移动 y1
                while (y1 <= y2 && !check(x1, y1, x2, y2))
                {
                    ++y1;
                }
                // 在满足条件的情况下,从 y1 到 y2 区域都是满足的,就不重复计算了
                res += (y2 - y1 + 1);
            }
        }
    }
    cout << res;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值