📜  总和可被 k 整除的最大矩形子矩阵

📅  最后修改于: 2021-09-22 10:07:46             🧑  作者: Mango

给定一个nxn整数矩阵。问题是找到总和可被给定值k整除的最大面积矩形子矩阵。

例子:

Input : mat[][] = { {1, 2, -1, -4},
                    {-8, -3, 4, 2},
                    {3, 8, 10, 1},
                    {-4, -1, 1, 7} }

        k = 5

Output : Area = 12
(Top, Left): (0, 0)
(Bottom, Right): (2, 3)
The sub-matrix is:
| 1, 2, -1, -4 |
| -8, -3, 4, 2 |
| 3, 8, 10, 1  |

朴素的方法:检查给定二维数组中每个可能的矩形,其总和可被“k”整除并打印最大的矩形。该解决方案需要 4 个嵌套循环,并且该解决方案的时间复杂度为 O(n^4)。

有效的方法:对于一维数组,其总和可被 k 整除的最长子数组可用于将时间复杂度降低到 O(n^3)。这个想法是一一固定左列和右列,并为每个左列和右列对的连续行找到总和可被“k”整除的最长子数组。我们基本上找到每个固定的左右列对的顶部和底部行号(它们是最大子矩阵的一部分)。要找到顶部和底部的行号,从左到右计算每行元素的总和,并将这些总和存储在一个数组中,比如 temp[]。所以 temp[i] 表示第 i 行从左到右的元素总和。现在,在 temp[] 上应用总和可被 k 一维算法整除的最长子数组,并获得总和可被 temp[] 的 ‘k’ 整除的最长子数组。此长度将是左和右作为边界列的最大可能长度。为左右列对设置“顶部”和“底部”行索引并计算面积。以类似的方式获取总和可被“k”整除的其他子矩阵的顶部、底部、左侧、右侧索引,并打印具有最大面积的那个。

C++
// C++ implementation to find largest rectangular
// sub-matrix having sum divisible by k
#include 
using namespace std;
 
#define SIZE 10
 
// function to find the longest subarray with sum divisible
// by k. The function stores starting and ending indexes of
// the subarray at addresses pointed by start and finish
// pointers respectively.
void longSubarrWthSumDivByK(int arr[], int n, int k,
                            int& start, int& finish)
{
    // unodered map 'um' implemented as
    // hash table
    unordered_map um;
 
    // 'mod_arr[i]' stores (sum[0..i] % k)
    int mod_arr[n];
    int curr_sum = 0, max = 0;
 
    // traverse arr[] and build up the
    // array 'mod_arr[]'
    for (int i = 0; i < n; i++) {
        curr_sum += arr[i];
 
        // as the sum can be negative, taking modulo twice
        mod_arr[i] = ((curr_sum % k) + k) % k;
    }
 
    for (int i = 0; i < n; i++) {
 
        // if true then sum(0..i) is divisible
        // by k
        if (mod_arr[i] == 0) {
 
            // update variables
            max = i + 1;
            start = 0;
            finish = i;
        }
 
        // if value 'mod_arr[i]' not present in 'um'
        // then store it in 'um' with index of its
        // first occurrence
        else if (um.find(mod_arr[i]) == um.end())
            um[mod_arr[i]] = i;
 
        else
            // if true, then update variables
            if (max < (i - um[mod_arr[i]])) {
            max = i - um[mod_arr[i]];
            start = um[mod_arr[i]] + 1;
            finish = i;
        }
    }
}
 
// function to find largest rectangular sub-matrix
// having sum divisible by k
void findLargestSubmatrix(int mat[SIZE][SIZE], int n, int k)
{
    // Variables to store the final output
    int finalLeft, finalRight, finalTop, finalBottom;
 
    int left, right, i, maxArea = 0;
    int temp[n], start, finish;
 
    // Set the left column
    for (left = 0; left < n; left++) {
 
        // Initialize all elements of temp as 0
        memset(temp, 0, sizeof(temp));
 
        // Set the right column for the left column
        // set by outer loop
        for (right = left; right < n; right++) {
 
            // Calculate sum between current left and
            // right for every row 'i'
            for (i = 0; i < n; ++i)
                temp[i] += mat[i][right];
 
            // The longSubarrWthSumDivByK() function sets
            // the values of 'start' and 'finish'. So
            // submatrix having sum divisible by 'k' between
            // (start, left) and (finish, right) which is
            // the largest submatrix with boundary columns
            // strictly as left and right.
            longSubarrWthSumDivByK(temp, n, k, start, finish);
 
            // Calculate current area and compare it with
            // maximum area so far. If maxArea is less, then
            // update maxArea and other output values
            if (maxArea < ((right - left + 1) *
                          (finish - start + 1))) {
                finalLeft = left;
                finalRight = right;
                finalTop = start;
                finalBottom = finish;
                maxArea = (right - left + 1) * (finish - start + 1);
            }
        }
    }
 
    // Print final values
    cout << "(Top, Left): (" << finalTop << ", "
         << finalLeft << ")\n";
    cout << "(Bottom, Right): (" << finalBottom << ", "
         << finalRight << ")\n";
    cout << "Area: " << maxArea;
}
 
// Driver program to test above functions
int main()
{
    int mat[SIZE][SIZE] = { { 1, 2, -1, -4 },
                            { -8, -3, 4, 2 },
                            { 3, 8, 10, 1 },
                            { -4, -1, 1, 7 } };
 
    int n = 4, k = 5;
    findLargestSubmatrix(mat, n, k);
 
    return 0;
}


Java
// Java implementation to find largest
// rectangular sub-matrix having sum
// divisible by k
import java.util.*;
 
class GFG{
 
static final int SIZE = 10;
static int start, finish;
 
// Function to find the longest subarray
// with sum divisible by k. The function
// stores starting and ending indexes of
// the subarray at addresses pointed by
// start and finish pointers respectively
static void longSubarrWthSumDivByK(int arr[],
                                   int n, int k)
{
     
    // unodered map 'um' implemented as
    // hash table
    HashMap um = new HashMap<>();
 
    // 'mod_arr[i]' stores (sum[0..i] % k)
    int []mod_arr = new int[n];
    int curr_sum = 0, max = 0;
     
    // Traverse arr[] and build up the
    // array 'mod_arr[]'
    for(int i = 0; i < n; i++)
    {
        curr_sum += arr[i];
         
        // As the sum can be negative,
        // taking modulo twice
        mod_arr[i] = ((curr_sum % k) + k) % k;
    }
 
    for(int i = 0; i < n; i++)
    {
         
        // If true then sum(0..i) is
        // divisible by k
        if (mod_arr[i] == 0)
        {
             
            // Update variables
            max = i + 1;
            start = 0;
            finish = i;
        }
 
        // If value 'mod_arr[i]' not present
        // in 'um' then store it in 'um' with
        // index of its first occurrence
        else if (!um.containsKey(mod_arr[i]))
            um.put(mod_arr[i], i);
 
        else
         
            // If true, then update variables
            if (max < (i - um.get(mod_arr[i])))
            {
                max = i - um.get(mod_arr[i]);
                start = um.get(mod_arr[i]) + 1;
                finish = i;
            }
    }
}
 
// Function to find largest rectangular
// sub-matrix having sum divisible by k
static void findLargestSubmatrix(int mat[][],
                                 int n, int k)
{
     
    // Variables to store the final output
    int finalLeft = 0, finalRight = 0,
        finalTop = 0, finalBottom = 0;
 
    int left, right, i, maxArea = 0;
    int []temp = new int[n];
 
    // Set the left column
    for(left = 0; left < n; left++)
    {
         
        // Initialize all elements of temp as 0
        Arrays.fill(temp, 0);
 
        // Set the right column for the left
        // column set by outer loop
        for(right = left; right < n; right++)
        {
             
            // Calculate sum between current
            // left and right for every row 'i'
            for(i = 0; i < n; ++i)
                temp[i] += mat[i][right];
 
            // The longSubarrWthSumDivByK() function
            // sets the values of 'start' and 'finish'.
            // So submatrix having sum divisible by 'k'
            // between (start, left) and (finish, right)
            // which is the largest submatrix with
            // boundary columns strictly as left and right.
            longSubarrWthSumDivByK(temp, n, k);
 
            // Calculate current area and compare it
            // with maximum area so far. If maxArea
            // is less, then update maxArea and other
            // output values
            if (maxArea < ((right - left + 1) *
                          (finish - start + 1)))
            {
                finalLeft = left;
                finalRight = right;
                finalTop = start;
                finalBottom = finish;
                maxArea = (right - left + 1) *
                        (finish - start + 1);
            }
        }
    }
 
    // Print final values
    System.out.print("(Top, Left): (" +
                     finalTop + ", " +
                     finalLeft + ")\n");
    System.out.print("(Bottom, Right): (" +
                     finalBottom + ", " +
                     finalRight + ")\n");
    System.out.print("Area: " + maxArea);
}
 
// Driver code
public static void main(String[] args)
{
    int [][]mat = { { 1, 2, -1, -4 },
                    { -8, -3, 4, 2 },
                    { 3, 8, 10, 1 },
                    { -4, -1, 1, 7 } };
 
    int n = 4, k = 5;
     
    findLargestSubmatrix(mat, n, k);
}
}
 
// This code is contributed by Amit Katiyar


C#
// C# implementation to find largest
// rectangular sub-matrix having sum
// divisible by k
using System;
using System.Collections.Generic;
 
class GFG{
 
//static readonly int SIZE = 10;
static int start, finish;
 
// Function to find the longest subarray
// with sum divisible by k. The function
// stores starting and ending indexes of
// the subarray at addresses pointed by
// start and finish pointers respectively
static void longSubarrWthSumDivByK(int []arr,
                                   int n, int k)
{
     
    // unodered map 'um' implemented as
    // hash table
    Dictionary um = new Dictionary();
 
    // 'mod_arr[i]' stores (sum[0..i] % k)
    int []mod_arr = new int[n];
    int curr_sum = 0, max = 0;
     
    // Traverse []arr and build up the
    // array 'mod_arr[]'
    for(int i = 0; i < n; i++)
    {
        curr_sum += arr[i];
         
        // As the sum can be negative,
        // taking modulo twice
        mod_arr[i] = ((curr_sum % k) + k) % k;
    }
 
    for(int i = 0; i < n; i++)
    {
         
        // If true then sum(0..i) is
        // divisible by k
        if (mod_arr[i] == 0)
        {
             
            // Update variables
            max = i + 1;
            start = 0;
            finish = i;
        }
 
        // If value 'mod_arr[i]' not present
        // in 'um' then store it in 'um' with
        // index of its first occurrence
        else if (!um.ContainsKey(mod_arr[i]))
            um.Add(mod_arr[i], i);
 
        else
         
            // If true, then update variables
            if (max < (i - um[mod_arr[i]]))
            {
                max = i - um[mod_arr[i]];
                start = um[mod_arr[i]] + 1;
                finish = i;
            }
    }
}
 
// Function to find largest rectangular
// sub-matrix having sum divisible by k
static void findLargestSubmatrix(int [,]mat,
                                 int n, int k)
{
     
    // Variables to store the readonly output
    int finalLeft = 0, finalRight = 0,
        finalTop = 0, finalBottom = 0;
 
    int left, right, i, maxArea = 0;
    int []temp;
 
    // Set the left column
    for(left = 0; left < n; left++)
    {
         
        // Initialize all elements of temp as 0
        temp = new int[n];
 
        // Set the right column for the left
        // column set by outer loop
        for(right = left; right < n; right++)
        {
             
            // Calculate sum between current
            // left and right for every row 'i'
            for(i = 0; i < n; ++i)
                temp[i] += mat[i,right];
 
            // The longSubarrWthSumDivByK() function
            // sets the values of 'start' and 'finish'.
            // So submatrix having sum divisible by 'k'
            // between (start, left) and (finish, right)
            // which is the largest submatrix with
            // boundary columns strictly as left and right.
            longSubarrWthSumDivByK(temp, n, k);
 
            // Calculate current area and compare it
            // with maximum area so far. If maxArea
            // is less, then update maxArea and other
            // output values
            if (maxArea < ((right - left + 1) *
                          (finish - start + 1)))
            {
                finalLeft = left;
                finalRight = right;
                finalTop = start;
                finalBottom = finish;
                maxArea = (right - left + 1) *
                        (finish - start + 1);
            }
        }
    }
 
    // Print readonly values
    Console.Write("(Top, Left): (" +
                   finalTop + ", " +
                  finalLeft + ")\n");
    Console.Write("(Bottom, Right): (" +
                    finalBottom + ", " +
                     finalRight + ")\n");
    Console.Write("Area: " + maxArea);
}
 
// Driver code
public static void Main(String[] args)
{
    int [,]mat = { { 1, 2, -1, -4 },
                   { -8, -3, 4, 2 },
                   { 3, 8, 10, 1 },
                   { -4, -1, 1, 7 } };
 
    int n = 4, k = 5;
     
    findLargestSubmatrix(mat, n, k);
}
}
 
// This code is contributed by Princi Singh


输出:

(Top, Left): (0, 0)
(Bottom, Right): (2, 3)
Area: 12

时间复杂度: O(n^3)。
辅助空间: O(n)。

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程