METEORAK - Editorial

PROBLEM LINK:

Practice
Contest

Author: Roman Rubanenko
Tester: Mahbub
Editorialist: Jingbo Shang

DIFFICULTY:

Medium

PREREQUISITES:

Dynamic Programming

PROBLEM:

Given a N * M matrix mat with some obstacles inside, deal with Q queries of finding the maximum empty submatrix in the submatrix mat[Li…Hi, *].

EXPLANATION:

First of all, we need to know how to deal with a single query. It is a classical dynamic programming problem, such as POJ 3494 Largest Submatrix with All 1’s. We can maintain an array up[1…N][1…M], where up[i][j] stands for the number of consecutive empty entries starting from (i, j) (inclusive). This array can be generated by the formula as following in O(NM) time.

if mat[i][j] is an obstacle:
    up[i][j] = 0
else
    up[i][j] = up[i - 1][j] + 1

To find the maximum empty submatrix, we only need to consider the extremal submatrix as shown in the following figure.

alt text

That is, the candidate (extremal) submatrix can’t extend itself in any directions. Extend means to enlarge the submatrix by adding an empty row or column to four directions (left, up, right, down).

Suppose the matrix is bound by the vertical line based on (i, j), its height should be up[i][j] and what we need is to calculate the horizonal boundary (left[i][j], right[i][j]), left and right are both exclusive. Because left and right are similar, we only focus on the left here. By the definition, left[i][j] is the first column k on the left side of j, such that up[i][k] is smaller than up[i][j]. There are two ways to calculate the left[i][li].
[/li]
First one, using the similar way to the disjoint set:

for i = 1 to N do {
    for j = 1 to M do {
        left[j] = j - 1;
        while (left[j] > 0 && up[i][left[j]] >= up[i][j]) {
            left[j] = left[left[j]];
        }
    }
}

This algorithm is O(NM alpha(M)), where alpha() is the inverse of the Ackermann function.

The second one is to use the monotonic stack (up[i][stack[ptr]] > up[i][stack[ptr - 1]] ) to solve this problem in O(NM) time.

for i =  1 to N do {
    top = 0;
    for j = 1 to M do {
        while (top > 0 && up[i][stack[top - 1]] >= up[i][j]) {
            top = top - 1;
        }
        if (top == 0) {
            left[i][j] = 0;
        } else {
            left[i][j] = stack[top - 1];
        }
        stack[top] = j;
        top = top + 1;
    }
}

Till now, with the help up, left and right, we can easily deal with a single query in O(NM) time (take all extremal empty submatrix and find the maximum). It is the time to start to deal with the orignal queries.

It is worth noting that, even for the query with L, H restrictions, we only need to consider the extremal submatrix too. Because if the best submatrix can extend in any direction (extend means adding an empty row or column to four directions to enlarge the “best submatrix”), the new area will not be worse after we extended that submatrix even if the extended parts are not in the query boundary.

Let’s define some auxiliary variables. Let c[l][r] is the maximal width of empty rectangle between l-th and r-th rows such that height is r - l + 1.

Initially, c[][] = 0. And then, for each non-obstacle entry (i, j), we update the extremal submatrix to their exact boundaries, as following:

c[i - up[i][j] + 1][i] = max{ c[i - up[i][j] + 1][i], right[i][j] - left[i][j] - 1 }

Finally, we extend the exact boundaries to their inner ones.

c[l][r] = max(c[l][r], c[x][y]), (1 <= x <= l <= r <= y <= N)

we can update c[][] in O(N^2), as following:

for len = N downto 2 do {
    for l = 1 to N - len + 1 do {
        r = l + len - 1;
        c[l + 1][r] = max{c[l + 1][r], c[l][r]};
        c[l][r - 1] = max{c[l][r - 1], c[l][r]};
    }
}

Similarly, we define d[l][r] as answer for corresponding query. The answer is max{c[i][j] * (j - i + 1), d[i][j - 1], d[i + 1][j]} (the bounary case is d[i][i-1] = 0). So, we should fill in the d[][] in order of increasing the length of interval j - i + 1, in similar way to that we get c[][].

AUTHOR’S AND TESTER’S SOLUTIONS:

Author’s solution can be found here.
Tester’s solution will be uploaded soon. here and here.

5 Likes

I’m just wondering was there worst case (for I/O) test case included?

worst test case:

1000 1000 1
1 1
1000000
100 1000
100 1000
...

where output should be

901000
901000
...

so we have ~9MB of input to read and ~7MB of output (correct me if I’m wrong) to output and there is no algorithm time included, are SPOJ servers able to that in timelimit of 1s?

4 Likes

No offense against the editorialist. I highly appreciate your efforts. I have some feedback which can be taken -vely but should be taken +vely.

I feel that the quality of this editorial needs improvement. Didn’t understand half of it.

“That is, the candidate submatrix can’t extend itself in any directions.” -
What is candidate matrix? What do you mean by “extend itself”. These concepts must be easy for you to understand but very difficult for others. Editorials are not seen by pro/expert coders but by struggling ones who could’nt solve the problem. So I feel that editorials should be as clear as possible rather than a namesake.

“It is worth noting that, even for the query with L, H restrictions, we only need to consider the extremal submatrix too. Because if the best submatrix can extend in any direction, the new area will not be worse after we extended that submatrix even if the extended parts are not in the query boundary.” - :frowning:

21 Likes

I think some drawings should be added to this editorial

I submitted same program in C++ and in C , my C solution got AC while C++ was giving TLE. Can anyone tell me why?
C++ sol http://www.codechef.com/viewsolution/3232566
C sol http://www.codechef.com/viewsolution/3232562

freopen("ip.txt","r",stdin); in C submission?

1 Like

O(n^2 m) passed.

OK, thanks for your suggestions, I have uploaded a figure to try to help you undertand this editorial (also some sentence are added to explain the “extend”). I hope you can learn the DP from this editorial :slight_smile:

5 Likes

Thanks for your suggestions. I have added a figure.

1 Like

That’s a good question to the Problem Setter.

oh really? can you share some details? It will be helpful to setter and tester to find stronger test cases I think.

alt text

9 Likes

it has been commented.and how it is going to affect the run time of the program…

http://www.codechef.com/viewsolution/3170885
I only used very fast I/O, nothing special besides that. Also, my solution’s runtime (other than I/O) depends only on the numbers N,M and not on some other properties of the given matrix or queries.

What? I fast scaned your solution, it looks like a O(NM) algorithm? You mean the “stack” for another M? It seems that all number will be in/out the stack only once. Therefore O(NM) in total?

No, there are three nested for-loops clearly visible in inline void racunajsol()

Thanks mate :slight_smile: …much appreciated

@shangjingbo The links to the tester and setter’s solution link back to the editorial page itself. Could you please correct the links.

1 Like

This problem is similar to “http://www.spoj.com/problems/ASTDPROB

How can a similar question come in a contest… Setters are supposed to set a new problem!!!
I mean a user can get many hints from stackoverflow, forums, and many other things for the problem if it is copied.

11 Likes

Certain O(NNM) solution have got accepted using fast input output :(.