📜  全为1的子矩阵数

📅  最后修改于: 2021-04-22 06:11:20             🧑  作者: Mango

给定一个仅包含0和1的N * N矩阵,任务是计算包含所有1的子矩阵的数量。

例子:

Input : arr[][] = {{1, 1, 1},
                   {1, 1, 1},
                   {1, 1, 1}}
Output : 36
Explanation: All the possible submatrices will have only 1s.
Since, there are 36 submatrices in total, ans = 36

Input : {{1, 1, 1},
                   {1, 0, 1},
                   {1, 1, 1}}
Output : 20

为简单起见,我们将说一个子矩阵从索引(R,C)开始,如果该特定索引是其左上角。

一个简单的解决方案是生成所有可能的子矩阵,然后检查其中的所有值是否均为1。如果对于一个子矩阵,所有元素均为1,则将最终答案的值加1。上述方法的时间复杂度为O(n 6 )。

更好的方法:为了优化处理过程,对于矩阵的每个索引,我们将尝试查找从该索引开始并包含全1的子矩阵的数量。

解决这个问题的第一步是创建一个矩阵“ p_arr”。

对于每个索引(R,C),如果arr [R] [C]等于1,则在p_arr [R] [C]中,我们将沿着行在单元格(R,C)的右边存储1的个数在遇到数组的第一个零或结尾加1之前为“ R”。
如果arr [R] [C]等于零,则p_arr [R] [C]也等于零。

为了创建此矩阵,我们将使用以下递归关系。

IF arr[R][C] is not 0
    p_arr[R][C] = p_arr[R][C+1] + 1
ELSE 
    p_arr[R][C] = 0

arr[][] = {{1, 0, 1, 1},
           {0, 1, 0, 1},
           {1, 1, 1, 0},
           {1, 0, 1, 1}}
p_arr[][] for above will look like
          {{1, 0, 2, 1},
           {0, 1, 0, 1},
           {3, 2, 1, 0},
           {1, 0, 2, 1}}

一旦有了所需的矩阵p_arr ,我们将继续进行下一步。逐列查看矩阵“ p_arr”。如果我们正在处理矩阵p_arr的j列,那么对于该列的每个元素“ i”,我们将尝试查找从单元格(i,j)开始全为1的子矩阵的数量。

为此,我们可以使用堆栈数据结构。

算法

  1. 初始化堆栈“ q”以存储被压入的元素的值以及此堆栈中被压入的元素数的count(C ij ),其值严格大于当前元素的值。我们将使用一对将两个数据捆绑在一起。
    用0初始化变量’to_sum’。在每个步骤中,此变量都会更新,以存储从该步骤中被推入的元素开始为全1的子矩阵的数量。因此,使用“ to_sum”,我们在每个步骤中将全为1的子矩阵的数量计数更新。
  2. 对于列“ j”,在任何步骤“ i”,我们都准备将p_arr [i] [j]压入堆栈。令Q t代表堆栈的最顶部元素,而C t代表先前推入堆栈的元素数量,其值大于堆栈的最顶端元素。在将元素“ p_arr [i] [j]”压入堆栈之前,如果堆栈不为空或最上面的元素大于要压入的数目,请继续弹出堆栈的最上面的元素,同时更新为to_sum等于to_sum + =(C t +1)*(Q t – p_arr [i] [j]) 。令C i,j表示大于在此堆栈中先前推送的当前元素的元素数。我们还需要跟踪C i,j 。因此,弹出一个元素之前,我们更新C I,J为C I,J + = C与to_sum沿
  3. 我们将答案更新为ans + = to_sum。
  4. 最后,将其与C i,j配对后,将其推入堆栈。

在O(n 2 )中创建前缀数组,对于每一列,我们将元素压入堆栈或仅弹出一次。因此,该算法的时间复杂度为O(n 2 )。

下面是上述方法的实现:

C++
// C++ program to count number of
// sub-matrices with all 1s
  
#include 
#include 
  
using namespace std;
  
#define n 3
  
// Function to find required prefix-count for
// each row from right to left
void findPrefixCount(int p_arr[][n], bool arr[][n])
{
    for (int i = 0; i < n; i++) {
        for (int j = n - 1; j >= 0; j--) {
  
            if (!arr[i][j])
                continue;
  
            if (j != n - 1)
                p_arr[i][j] += p_arr[i][j + 1];
  
            p_arr[i][j] += (int)arr[i][j];
        }
    }
}
  
// Function to count the number of
// sub-matrices with all 1s
int matrixAllOne(bool arr[][n])
{
    // Array to store required prefix count of
    // 1s from right to left for boolean array
    int p_arr[n][n] = { 0 };
  
    findPrefixCount(p_arr, arr);
  
    // variable to store the final answer
    int ans = 0;
  
    /*  Loop to evaluate each column of
        the prefix matrix uniquely.
        For each index of a column we will try to
        determine the number of sub-matrices
        starting from that index
        and has all 1s */
    for (int j = 0; j < n; j++) {
  
        int i = n - 1;
  
        /*  Stack to store elements and the count
            of the numbers they popped
              
            First part of pair will be the
            value of inserted element.
            Second part will be the count
            of the number of elements pushed
            before with a greater value */
        stack > q;
  
        // variable to store the number of
        // submatrices with all 1s
        int to_sum = 0;
  
        while (i >= 0) {
  
            int c = 0;
  
            while (q.size() != 0 and q.top().first > p_arr[i][j]) {
  
                to_sum -= (q.top().second + 1) * 
                            (q.top().first - p_arr[i][j]);
  
                c += q.top().second + 1;
                q.pop();
            }
  
            to_sum += p_arr[i][j];
  
            ans += to_sum;
  
            q.push({ p_arr[i][j], c });
  
            i--;
        }
    }
  
    return ans;
}
  
// Driver Code
int main()
{
    bool arr[][n] = { { 1, 1, 0 },
                      { 1, 0, 1 },
                      { 0, 1, 1 } };
  
    cout << matrixAllOne(arr);
  
    return 0;
}


Java
// Java program to count number of
// sub-matrices with all 1s
import java.util.*;
class GFG 
{
      
static int n = 3;
static class pair
{ 
    int first, second; 
    public pair(int first, int second) 
    { 
        this.first = first; 
        this.second = second; 
    } 
} 
  
// Function to find required prefix-count for
// each row from right to left
static void findPrefixCount(int p_arr[][], 
                            boolean arr[][])
{
    for (int i = 0; i < n; i++)
    {
        for (int j = n - 1; j >= 0; j--) 
        {
            if (!arr[i][j])
                continue;
  
            if (j != n - 1)
                p_arr[i][j] += p_arr[i][j + 1];
  
            p_arr[i][j] += arr[i][j] == true ? 1 : 0;
        }
    }
}
  
// Function to count the number of
// sub-matrices with all 1s
static int matrixAllOne(boolean arr[][])
{
    // Array to store required prefix count of
    // 1s from right to left for boolean array
    int [][]p_arr = new int[n][n];
  
    findPrefixCount(p_arr, arr);
  
    // variable to store the final answer
    int ans = 0;
  
    /* Loop to evaluate each column of
        the prefix matrix uniquely.
        For each index of a column we will try to
        determine the number of sub-matrices
        starting from that index
        and has all 1s */
    for (int j = 0; j < n; j++) 
    {
        int i = n - 1;
  
        /* Stack to store elements and the count
            of the numbers they popped
              
            First part of pair will be the
            value of inserted element.
            Second part will be the count
            of the number of elements pushed
            before with a greater value */
        Stack q = new Stack();
  
        // variable to store the number of
        // submatrices with all 1s
        int to_sum = 0;
  
        while (i >= 0) 
        {
            int c = 0;
  
            while (q.size() != 0 &&
                   q.peek().first > p_arr[i][j]) 
            {
                to_sum -= (q.peek().second + 1) * 
                          (q.peek().first - p_arr[i][j]);
  
                c += q.peek().second + 1;
                q.pop();
            }
  
            to_sum += p_arr[i][j];
  
            ans += to_sum;
  
            q.add(new pair(p_arr[i][j], c));
  
            i--;
        }
    }
    return ans;
}
  
// Driver Code
public static void main(String[] args)
{
    boolean arr[][] = { { true, true, false },
                        { true, false, true },
                        { false, true, true } };
  
    System.out.println(matrixAllOne(arr));
}
}
  
// This code is contributed by PrinciRaj1992


Python3
# Python3 program to count the number 
# of sub-matrices with all 1s 
  
# Function to find required prefix-count 
# for each row from right to left 
def findPrefixCount(p_arr, arr): 
  
    for i in range(0, n): 
        for j in range(n - 1, -1, -1): 
  
            if not arr[i][j]: 
                continue
  
            if j != n - 1: 
                p_arr[i][j] += p_arr[i][j + 1] 
  
            p_arr[i][j] += arr[i][j] 
          
# Function to count the number of 
# sub-matrices with all 1s 
def matrixAllOne(arr): 
  
    # Array to store required prefix count of 
    # 1s from right to left for boolean array 
    p_arr = [[0 for i in range(n)] for j in range(n)] 
  
    findPrefixCount(p_arr, arr) 
  
    # variable to store the final answer 
    ans = 0
  
    # Loop to evaluate each column of 
    # the prefix matrix uniquely. 
    # For each index of a column we will try to 
    # determine the number of sub-matrices 
    # starting from that index and has all 1s
    for j in range(0, n): 
  
        i = n - 1
  
        # Stack to store elements and the count 
        # of the numbers they popped 
              
        # First part of pair will be the 
        # value of inserted element. 
        # Second part will be the count 
        # of the number of elements pushed 
        # before with a greater value */
        q = [] 
  
        # variable to store the number of 
        # submatrices with all 1s 
        to_sum = 0
  
        while i >= 0: 
  
            c = 0
            while len(q) != 0 and q[-1][0] > p_arr[i][j]: 
  
                to_sum -= (q[-1][1] + 1) * \
                            (q[-1][0] - p_arr[i][j]) 
  
                c += q[-1][1] + 1
                q.pop() 
              
            to_sum += p_arr[i][j] 
            ans += to_sum 
  
            q.append((p_arr[i][j], c)) 
            i -= 1
          
    return ans 
  
# Driver Code 
if __name__ == "__main__":
  
    arr = [[1, 1, 0], [1, 0, 1], [0, 1, 1]] 
                  
    n = 3
    print(matrixAllOne(arr)) 
  
# This code is contributed by Rituraj Jain


C#
// C# program to count number of
// sub-matrices with all 1s
using System;
using System.Collections.Generic;
      
class GFG 
{
static int n = 3;
class pair
{ 
    public int first, second; 
    public pair(int first, int second) 
    { 
        this.first = first; 
        this.second = second; 
    } 
} 
  
// Function to find required prefix-count for
// each row from right to left
static void findPrefixCount(int [,]p_arr, 
                            Boolean [,]arr)
{
    for (int i = 0; i < n; i++)
    {
        for (int j = n - 1; j >= 0; j--) 
        {
            if (!arr[i, j])
                continue;
  
            if (j != n - 1)
                p_arr[i, j] += p_arr[i, j + 1];
  
            p_arr[i, j] += arr[i, j] == true ? 1 : 0;
        }
    }
}
  
// Function to count the number of
// sub-matrices with all 1s
static int matrixAllOne(Boolean [,]arr)
{
    // Array to store required prefix count of
    // 1s from right to left for boolean array
    int [,]p_arr = new int[n, n];
  
    findPrefixCount(p_arr, arr);
  
    // variable to store the final answer
    int ans = 0;
  
    /*  Loop to evaluate each column of
        the prefix matrix uniquely.
        For each index of a column we will try to
        determine the number of sub-matrices
        starting from that index
        and has all 1s */
    for (int j = 0; j < n; j++) 
    {
        int i = n - 1;
  
        /*  Stack to store elements and the count
            of the numbers they popped
              
            First part of pair will be the
            value of inserted element.
            Second part will be the count
            of the number of elements pushed
            before with a greater value */
        Stack q = new Stack();
  
        // variable to store the number of
        // submatrices with all 1s
        int to_sum = 0;
  
        while (i >= 0) 
        {
            int c = 0;
  
            while (q.Count != 0 &&
                   q.Peek().first > p_arr[i, j]) 
            {
                to_sum -= (q.Peek().second + 1) * 
                          (q.Peek().first - p_arr[i, j]);
  
                c += q.Peek().second + 1;
                q.Pop();
            }
  
            to_sum += p_arr[i, j];
  
            ans += to_sum;
  
            q.Push(new pair(p_arr[i, j], c));
  
            i--;
        }
    }
    return ans;
}
  
// Driver Code
public static void Main(String[] args)
{
    Boolean [,]arr = {{ true, true, false },
                      { true, false, true },
                      { false, true, true }};
  
    Console.WriteLine(matrixAllOne(arr));
}
}
  
// This code is contributed by 29AjayKumar


输出:
10

时间复杂度:O(N 2 )