0

Context:


I have a matrix of x and y dimensions.

The left-most column and the top-most row are considered the edges of the North-West ocean.

The right-most column and the bottom-most row are considered the edges of the South-East ocean.

Starting from any square, on any edge, the water will flow into adjacent squares only if:

current square of water >= neighbor square

If the water reaches one of the edges of the opposite ocean than the starting one, it forms a path.

If the water gets stuck and can't go anywhere, or if it doesn't reach one of the opposite edges, it is NOT a path.

What I need to do is check each path, for each ocean and then overlap them and give the total number of overlapping squares and their coordinates.


Personally, I have approached this, thinking of it like a height map. I have made 2 functions, using a queue and sort-of a BFS search. The problem I have is, I cannot seem to get the exact correct squares to highlightenter image description here

This is the matrix that is giving me problems.

On the NW path: Top row: If the starting point is anything other than 6, it won't work cause of higher numbers around. 6 will be highlighted

Left column: 1 does not work, but 20 does, and it can take it all the way to 16 reaching the SE ocean. Valid path.

So the highlighted squares should be: 6, 20, 19, 18, 17, 16.

NW Traversal (start from the first row or first column and stop only if it reaches the SE edge)

from collections import deque

def traverse_nw(table):
    max_i = len(table) - 1
    max_j = len(table[0]) - 1
    visited_nw = [[False for _ in range(max_j + 1)] for _ in range(max_i + 1)]
    reaches_se = [[False for _ in range(max_j + 1)] for _ in range(max_i + 1)]  # Track paths reaching SE

    # Initialize queue with all cells in the first row and first column (NW ocean)
    queue = deque([(0, j) for j in range(max_j + 1)] + [(i, 0) for i in range(1, max_i + 1)])

    while queue:
        i, j = queue.popleft()

        if visited_nw[i][j]:
            continue
        
        # Move to neighbors if their height is lower or equal
        valid_neighbors = []
        if i > 0 and not visited_nw[i - 1][j] and table[i][j] >= table[i - 1][j]:  # Up
            valid_neighbors.append((i - 1, j))
        if i < max_i and not visited_nw[i + 1][j] and table[i][j] >= table[i + 1][j]:  # Down
            valid_neighbors.append((i + 1, j))
        if j > 0 and not visited_nw[i][j - 1] and table[i][j] >= table[i][j - 1]:  # Left
            valid_neighbors.append((i, j - 1))
        if j < max_j and not visited_nw[i][j + 1] and table[i][j] >= table[i][j + 1]:  # Right
            valid_neighbors.append((i, j + 1))

        # Only mark a cell as visited if it has valid neighbors
        if valid_neighbors:
            visited_nw[i][j] = True

        # Check if the current cell has reached the SE edge (bottom row or rightmost column)
        if (i == max_i or j == max_j) and visited_nw[i][j]:
            reaches_se[i][j] = True  # This path reaches the SE edge

        # Only proceed to mark a cell as leading to SE if one of its valid neighbors leads to SE
        for ni, nj in valid_neighbors:
            queue.append((ni, nj))
            if reaches_se[i][j]:  # If the current cell can reach SE, propagate that information
                reaches_se[ni][nj] = True

    return visited_nw, reaches_se

SE Traversal (start from the last row or last column and stop only if it reaches the NW edge)

def traverse_se(table):
    max_i = len(table) - 1
    max_j = len(table[0]) - 1
    
    # Create a grid to track visited cells for SE traversal
    visited_se = [[False for _ in range(max_j + 1)] for _ in range(max_i + 1)]
    
    # Initialize queue with all cells in the last row and last column (SE ocean)
    queue = deque([(max_i, j) for j in range(max_j + 1)] + [(i, max_j) for i in range(max_i)])

    # Create a grid to mark paths that can reach the NW ocean
    reaches_nw = [[False for _ in range(max_j + 1)] for _ in range(max_i + 1)]

    # BFS to explore SE traversal
    while queue:
        i, j = queue.popleft()

        # If the cell is already visited, skip it
        if visited_se[i][j]:
            continue

        # Mark the cell as visited
        visited_se[i][j] = True

        # Check if the current cell has reached the NW edge (top row or leftmost column)
        if i == 0 or j == 0:
            reaches_nw[i][j] = True  # This path reaches the NW ocean

        # Move to neighboring cells if their height is lower or equal
        if i > 0 and not visited_se[i - 1][j] and table[i][j] >= table[i - 1][j]:  # Up
            queue.append((i - 1, j))
        if i < max_i and not visited_se[i + 1][j] and table[i][j] >= table[i + 1][j]:  # Down
            queue.append((i + 1, j))
        if j > 0 and not visited_se[i][j - 1] and table[i][j] >= table[i][j - 1]:  # Left
            queue.append((i, j - 1))
        if j < max_j and not visited_se[i][j + 1] and table[i][j] >= table[i][j + 1]:  # Right
            queue.append((i, j + 1))

    return visited_se, reaches_nw

If you got anything let me know, thanks in advance

4
  • So the expected result should be 6, 20, 19, 18, 17, 16. What is the current result and how is it obtained from the given functions? Commented Oct 9, 2024 at 11:30
  • Why "Only mark a cell as visited if it has valid neighbors"? I think it does introduce unwanted behaviour. For example if you traverse the algorithm by hand for the first row, 1 and 3 will not be marked as visited. Commented Oct 9, 2024 at 11:50
  • The current result are the highlighted squares in the picture Commented Oct 9, 2024 at 12:33
  • @MatthiasMertens you are right, i changed that. Now, all the squares on the edges are highlighted Commented Oct 9, 2024 at 12:39

1 Answer 1

1

I think a recursive DFS approach is easier since there cannot be loops and you can be sure that the condition is checked completely if the cell was visited:

table = [[1,2,3,4,5,6], [20,21,22,23,24,7], [19,32,33,34,25,8], [18,31,36,35,26,9],[17,30,29,28,27,10], [16,15,14,13,12,11]]
visited = [[False for _ in range(6)] for _ in range(6)]
reaches_se = [[False for _ in range(6)] for _ in range(6)]


for coord in ([(0, j) for j in range(6)] + [(i, 0) for i in range(1,6)]):
    reached_condition(coord, is_SE)

print(reaches_se)


def reached_condition(coord, condition):
    (i, j) = coord
    if visited[i][j]:
        return reaches_se[i][j]
    
    reached = False
    if condition(coord): reached = True
    
    valid_neighbors = get_valid_neighbors(coord)
    for coord_n in valid_neighbors:
        if reached_condition(coord_n, condition): reached = True
    
    if reached: reaches_se[i][j] = True;
    visited[i][j] = True
    return reached
    
def get_valid_neighbors(coord):
    (i, j) = coord
    valid_neighbors = []
    if i > 0 and table[i][j] >= table[i - 1][j]:  # Up
        valid_neighbors.append((i - 1, j))
    if i < 5 and table[i][j] >= table[i + 1][j]:  # Down
        valid_neighbors.append((i + 1, j))
    if j > 0 and table[i][j] >= table[i][j - 1]:  # Left
        valid_neighbors.append((i, j - 1))
    if j < 5 and table[i][j] >= table[i][j + 1]:  # Right
        valid_neighbors.append((i, j + 1))
    return valid_neighbors

def is_NW(coord):
    (i, j) = coord
    if i == 0 or j == 0: return True
    else: return False

def is_SE(coord):
    (i, j) = coord
    if i == 5 or j == 5: return True
    else: return False

You might need to apply some extra care for the edge cases (0, max_j) and (max_i, 0).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.