包含全零的子矩阵的数量 [英] Number of submatricies containing all zeros

查看:59
本文介绍了包含全零的子矩阵的数量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有没有一种方法可以找到许多包含全零的矩形子矩阵,其复杂度小于O(n ^ 3),其中n是给定矩阵的维数?

Is there a way to find a number of rectangular submatrices containing all zeros with a complexity smaller than O(n^3), where n is the dimension of given matrix?

推荐答案

这是解决方案 O(n²log n).

首先,让我们将主要问题转换为以下内容:

Here is a solution O(n² log n).

First, let's convert the main problem to something like this:

对于给定的直方图,找到包含全零的子矩阵的数量.

For given histogram, find the number of submatrices containing all zeros.

对于每个位置,计算从该位置开始且仅包含零的列的高度.

For each position calculate the height of column that start on that position and contain only zeros.

示例:

10010    01101
00111    12000
00001 -> 23110
01101    30020
01110    40001

可以在O(n²)中轻松找到它.

It can be easily find in O(n²).

for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++)
        up[i][j] = arr[i][j] ? 0 : 1 + up[i - 1][j];

现在,我们可以将每一行视为具有给定高度的直方图.

Now we can consider each row as histogram with given heights.

我们的目标是从左向右移动所有高度,并且在每一步上我们将更新数组L.每个高度的此数组将包含最大宽度,以便我们可以从当前位置到左侧并以给定高度创建一个具有此宽度的矩形.

Our goal is to travel all heights from left to right, and on each step we are going to update array L. This array for each height is going to contain maximum widths so that we can make a rectangle of this width from current position, to the left and of given height.

考虑示例:

0
0   0
0 000
00000   -> heights: 6 3 4 4 5 2
000000
000000

L[6]:   1     0     0     0     0     0
L[5]:   1     0     0     0     1     0
L[4]:   1     0     1     2     3     0
L[3]:   1     2     3     4     5     0
L[2]:   1     2     3     4     5     6
L[1]:   1     2     3     4     5     6
steps:  1     2     3     4     5     6

如您所见,如果我们将所有这些数字相加,我们将收到给定直方图的答案.

As you can see if we add all those numbers we will receive an answer for given histogram.

我们可以简单地更新O(n)中的数组L,但是我们也可以通过使用段树(具有惰性传播)在O(log n)中进行更新,该段树可以添加间隔,在间隔中设置值并从中获取和间隔.

We can simply update array L in O(n), however we can also do it in O(log n) by using segment tree (with lazy propagation) that can add in interval, set value in interval and get sum from interval.

在每个步骤中,我们只需在区间[1,height]上加1,并在区间[height + 1,maxHeight]中设置0,然后从区间[1,maxHeight]中求和.

In each step we just add 1 to interval [1, height] and set 0 in interval[height + 1, maxHeight] and get sum from interval [1, maxHeight].

height-直方图中当前列的高度.

height - height of current column in histogram.

maxHeight-直方图中列的最大高度.

maxHeight - maximum height of column in histogram.

那就是您如何获得O(n²* log n)解决方案:)

And thats how you can get O(n² * log n) solution :)

const int MAXN = 1000;
int n;
int arr[MAXN + 5][MAXN + 5]; // stores given matrix
int up[MAXN + 5][MAXN + 5]; // heights of columns of zeros
long long answer;

long long calculate(int *h, int maxh) { // solve it for histogram
    clearTree();

    long long result = 0;
    for(int i = 1; i <= n; i++) {
        add(1, h[i]); // add 1 to [1, h[i]]
        set(h[i] + 1, maxh); // set 0 in [h[i] + 1, maxh];
        result += query(); // get sum from [1, maxh]
    }

    return result;
}
int main() {
    ios_base::sync_with_stdio(0);
    cin >> n;

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            cin >> arr[i][j]; // read the data

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            up[i][j] = arr[i][j] ? 0 : 1 + up[i - 1][j]; // calculate values of up

    for(int i = 1; i <= n; i++)
        answer += calculate(up[i], i); // calculate for each row

    cout << answer << endl;
}

这是代码的开始,段树:

#include <iostream>
using namespace std;

// interval-interval tree that stores sums

const int p = 11;
int sums[1 << p];
int lazy[1 << p];
int need[1 << p];
const int M = 1 << (p - 1);

void update(int node) {
    if(need[node] == 1) { // add
        sums[node] += lazy[node];
        if(node < M) {
            need[node * 2] = need[node * 2] == 2 ? 2 : 1;
            need[node * 2 + 1] = need[node * 2 + 1] == 2 ? 2 : 1;
            lazy[node * 2] += lazy[node] / 2;
            lazy[node * 2 + 1] += lazy[node] / 2;
        }
    } else if(need[node] == 2) { // set
        sums[node] = lazy[node];
        if(node < M) {
            need[node * 2] = need[node * 2 + 1] = 2;
            lazy[node * 2] = lazy[node] / 2;
            lazy[node * 2 + 1] = lazy[node] / 2;
        }
    }
    need[node] = 0;
    lazy[node] = 0;
}

void insert(int node, int l, int r, int lq, int rq, int value, int id) {
    update(node);
    if(lq <= l && r <= rq) {
        need[node] = id;
        lazy[node] = value * (r - l + 1);
        update(node);
        return;
    }
    int mid = (l + r) / 2;
    if(lq <= mid) insert(node * 2, l, mid, lq, rq, value, id);
    if(mid + 1 <= rq) insert(node * 2 + 1, mid + 1, r, lq, rq, value, id);
    sums[node] = sums[node * 2] + sums[node * 2 + 1];
}


int query() {
    return sums[1]; // we only need to know sum of the whole interval
}

void clearTree() {
    for(int i = 1; i < 1 << p; i++)
        sums[i] = lazy[i] = need[i] = 0;
}

void add(int left, int right) {
    insert(1, 0, M - 1, left, right, 1, 1);
}

void set(int left, int right) {
    insert(1, 0, M - 1, left, right, 0, 2);
}

// end of the tree

这篇关于包含全零的子矩阵的数量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆