Map of Highest Peak

Updated on 16 June, 2025
Map of Highest Peak header image

Problem Statement

In the given problem, we are provided with an integer matrix isWater of dimensions m x n, where each element of the matrix signifies whether a cell is water or land. Specifically, a value of 1 indicates a water cell, and 0 indicates a land cell. Our goal is to assign a non-negative height to each cell. Water cells must have a height of zero and land cells should be assigned heights following a specific rule: The absolute height difference between any two adjacent cells must not exceed one. An adjacent cell is one that lies directly north, south, east, or west of another cell.

The objective is to determine an assignment of heights where the maximum height of any land cell is as large as possible, given the constraints. This task is a bit tricky as the solution should not only follow the adjacency rule but also maximize the height difference to achieve the tallest possible configuration without breaking the adjacency rule on cell heights.

Examples

Example 1

Input:

isWater = [[0,1],[0,0]]

Output:

[[1,0],[2,1]]

Explanation:

The image shows the assigned heights of each cell.
The blue cell is the water cell, and the green cells are the land cells.

Example 2

Input:

isWater = [[0,0,1],[1,0,0],[0,0,0]]

Output:

[[1,1,0],[0,1,1],[1,2,2]]

Explanation:

A height of 2 is the maximum possible height of any assignment.
Any height assignment that has a maximum height of 2 while still meeting the rules will also be accepted.

Constraints

  • m == isWater.length
  • n == isWater[i].length
  • 1 <= m, n <= 1000
  • isWater[i][j] is 0 or 1.
  • There is at least one water cell.

Approach and Intuition

Given the constraints and rules, let's break down the approach:

  1. Initialization:

    • We begin with initializing all water cells with a height of 0 since that is explicitly stated in the problem.
    • Since the goal is to maximize the height of land cells, we can utilize a BFS (Breadth-First Search) approach starting from all water cells.
  2. Implementing BFS:

    • Enqueue all water cells as starting points in our BFS, as these are the only cells with a predetermined height.
    • Intuitively, the height of any land cell will be determined based on its shortest distance to the nearest water cell, given that each move away from a water cell increments the height by one.
  3. Processing Neighbors:

    • For each cell being processed in the BFS, check its four possible neighbors (north, south, east, and west).
    • If a neighbor is a land cell and hasn't been assigned a height yet, or if a smaller height can be assigned (ensuring each step away from water increments height by one), update its height and enqueue it for further processing.
  4. Height Maximization:

    • Since BFS processes levels in waves from the starting points, land cells farther from the initial water cells naturally achieve higher heights as the BFS progresses. This is how the height maximization is implicitly handled.
    • Each land cell will essentially receive a height equal to its shortest path (in terms of steps) to the nearest water cell.
  5. Termination:

    • The BFS completes when all possible cells are visited and no further updates are needed. This ensures that all cells are set to the optimal heights adhering to the conditions provided.

Edge Cases and Complexity Considerations: * For mazes where land surrounds a small area of water or vice versa, the BFS efficiently handles incrementally assigning heights. * As BFS is linear in the number of cells, the given constraints (up to 1000x1000 matrix) will operate in feasible time.

Through this systematic and iterative approach, the proposed BFS strategy optimally assigns heights while ensuring that each land cell's height is as tall as feasible, given its proximity to water cells.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    vector<vector<int>> generateHeights(vector<vector<int>>& waterMap) {
        const int m = waterMap.size();
        const int n = waterMap[0].size();
        const int HIGH_VALUE = m * n;

        vector<vector<int>> heights(m, vector<int>(n, HIGH_VALUE));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (waterMap[i][j]) {
                    heights[i][j] = 0;
                }
            }
        }

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int lowest_neighbor = HIGH_VALUE;

                int upRow = i - 1;
                int upCol = j;
                if (checkCell(upRow, upCol, m, n)) {
                    lowest_neighbor = min(lowest_neighbor, heights[upRow][upCol]);
                }

                int leftRow = i;
                int leftCol = j - 1;
                if (checkCell(leftRow, leftCol, m, n)) {
                    lowest_neighbor = min(lowest_neighbor, heights[leftRow][leftCol]);
                }

                heights[i][j] = min(heights[i][j], lowest_neighbor + 1);
            }
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                int lowest_neighbor = HIGH_VALUE;

                int downRow = i + 1;
                int downCol = j;
                if (checkCell(downRow, downCol, m, n)) {
                    lowest_neighbor = min(lowest_neighbor, heights[downRow][downCol]);
                }

                int rightRow = i;
                int rightCol = j + 1;
                if (checkCell(rightRow, rightCol, m, n)) {
                    lowest_neighbor = min(lowest_neighbor, heights[rightRow][rightCol]);
                }

                heights[i][j] = min(heights[i][j], lowest_neighbor + 1);
            }
        }

        return heights;
    }

private:
    bool checkCell(int row, int col, int maxRow, int maxCol) {
        return row >= 0 && col >= 0 && row < maxRow && col < maxCol;
    }
};

In the provided C++ solution for generating a height map from a water map, you work with a 2D grid. The objective is to assign heights to each cell starting from zero at water cells and increasing the height for land cells, ensuring that every cell's height is minimal based on proximity to the nearest water cell.

  • Start by initializing the height of every cell to a large value.
  • Set the heights of water cells (cells containing 1) in the waterMap to zero.
  • Process each cell in the grid to compute its height based on the heights of its neighbors. This involves:
    • Checking available neighboring cells (up and left) and updating the current cell’s height to be one plus the minimum height of these neighbors, provided the current cell is not already set to zero.
    • Repeat the height determination from the opposite direction—down and right neighbors to ensure each cell is assigned the lowest possible value considering all four potential neighbor directions.

The solution leverages the helper function checkCell to verify if a given cell index (row, column) is valid. This prevents out-of-bound errors by ensuring indices are within the grid limits.

By adapting the heights after each full grid scan, and then re-evaluating to potentially lower them further in a second pass, the function aims to output a grid (heights) where each land cell has the smallest possible height that maintains integer height differences between any accessible neighboring cells, adhering to constraints driven by proximity to the nearest water cells.

java
public class Solution {

    public int[][] calculatePeaks(int[][] waterMap) {
        int rowNum = waterMap.length;
        int colNum = waterMap[0].length;
        final int MAX_DIST = rowNum * colNum; // Used as initial distance

        int[][] peakHeights = new int[rowNum][colNum];
        for (int[] heightRow : peakHeights) {
            Arrays.fill(heightRow, MAX_DIST);
        }

        for (int i = 0; i < rowNum; i++) {
            for (int j = 0; j < colNum; j++) {
                if (waterMap[i][j] == 1) {
                    peakHeights[i][j] = 0;
                }
            }
        }

        for (int i = 0; i < rowNum; i++) {
            for (int j = 0; j < colNum; j++) {
                int minDist = MAX_DIST; 

                if (cellExists(i - 1, j, rowNum, colNum)) {
                    minDist = Math.min(minDist, peakHeights[i - 1][j]);
                }
                if (cellExists(i, j - 1, rowNum, colNum)) {
                    minDist = Math.min(minDist, peakHeights[i][j - 1]);
                }

                peakHeights[i][j] = Math.min(peakHeights[i][j], minDist + 1);
            }
        }

        for (int i = rowNum - 1; i >= 0; i--) {
            for (int j = colNum - 1; j >= 0; j--) {
                int minDist = MAX_DIST;

                if (cellExists(i + 1, j, rowNum, colNum)) {
                    minDist = Math.min(minDist, peakHeights[i + 1][j]);
                }
                if (cellExists(i, j + 1, rowNum, colNum)) {
                    minDist = Math.min(minDist, peakHeights[i][j + 1]);
                }

                peakHeights[i][j] = Math.min(peakHeights[i][j], minDist + 1);
            }
        }

        return peakHeights;
    }

    private boolean cellExists(int i, int j, int maxRow, int maxCol) {
        return i >= 0 && j >= 0 && i < maxRow && j < maxCol;
    }
}

This Java solution focuses on calculating peak heights from a given water map using a multi-pass flood fill algorithm.

Initial set up involves creating an array, peakHeights, initialized to a high value representing maximum distances. Cells in waterMap containing water (represented by 1) are set to 0 in peakHeights, indicating the shortest distance.

The program executes two main loops:

  • The first pass progresses from the top-left towards the bottom-right. For each cell, it computes the minimum distance from either the top or the left neighboring cell using the previously calculated values.
  • The second pass moves from the bottom-right towards the top-left, this time comparing each cell's distance with its bottom and right neighbors. This ensures each cell's height is the minimum value reached from any adjacent filled cell.

The method cellExists() checks boundary conditions to prevent access outside the array bounds.

By iterating in two directions, the program effectively spreads out the height values from the water sources, allowing it to calculate peak distributions based on proximity to the nearest water source accurately. The method returns the filled peakHeights array representing distances to the nearest water-filled cell.

python
class Solution:
    def calculatePeak(self, water_map: List[List[int]]) -> List[List[int]]:
        num_rows = len(water_map)
        num_cols = len(water_map[0])
        max_distance = num_rows * num_cols

        heights = [[max_distance] * num_cols for _ in range(num_rows)]

        for i in range(num_rows):
            for j in range(num_cols):
                if water_map[i][j] == 1:
                    heights[i][j] = 0

        for i in range(num_rows):
            for j in range(num_cols):
                if i > 0:
                    heights[i][j] = min(heights[i][j], heights[i-1][j] + 1)
                if j > 0:
                    heights[i][j] = min(heights[i][j], heights[i][j-1] + 1)

        for i in range(num_rows-1, -1, -1):
            for j in range(num_cols-1, -1, -1):
                if i < num_rows - 1:
                    heights[i][j] = min(heights[i][j], heights[i+1][j] + 1)
                if j < num_cols - 1:
                    heights[i][j] = min(heights[i][j], heights[i][j+1] + 1)

        return heights

    def is_within_bounds(self, x: int, y: int, height: int, width: int) -> bool:
        return 0 <= x < height and 0 <= y < width

The provided Python code defines a class Solution with methods to calculate the height map of the highest peaks relative to water locations given in a 2D grid. The code consists of two main parts:

  1. calculatePeak Method:

    • Initializes the heights array with a very high value for each cell, to be minimized later.
    • Sets the height of water locations (value 1 in the water_map) to 0.
    • Uses a two-pass algorithm to propagate the minimum possible heights from the known water locations:
      • The first loop moves left to right and top to bottom, adjusting heights based on the minimum height of the top and left neighbors plus one.
      • The second loop moves in the opposite direction (right to left and bottom to top) updating heights based on bottom and right neighbors.
    • This method returns the height map as a 2D list after both passes, representing the shortest distance to the nearest water cell.
  2. is_within_bounds Method:

    • Checks whether a given position is within the bounds of the grid. This helper function validates if coordinates (x, y) are within a grid of height and width.

Review the approach outlined in the calculatePeak method to ensure it meets the requirements of creating a height map around water cells, capturing the shortest path the terrain has to rise from any given land cell to the nearest water cell. The second method, is_within_bounds, is generally a utility to help with boundary checks but is unused in the current solution provided.

Comments

No comments yet.