<p>There is an <code>m x n</code> rectangular island that borders both the <strong>Pacific Ocean</strong> and <strong>Atlantic Ocean</strong>. The <strong>Pacific Ocean</strong> touches the island&#39;s left and top edges, and the <strong>Atlantic Ocean</strong> touches the island&#39;s right and bottom edges.</p>

<p>The island is partitioned into a grid of square cells. You are given an <code>m x n</code> integer matrix <code>heights</code> where <code>heights[r][c]</code> represents the <strong>height above sea level</strong> of the cell at coordinate <code>(r, c)</code>.</p>

<p>The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell&#39;s height is <strong>less than or equal to</strong> the current cell&#39;s height. Water can flow from any cell adjacent to an ocean into the ocean.</p>

<p>Return <em>a <strong>2D list</strong> of grid coordinates </em><code>result</code><em> where </em><code>result[i] = [r<sub>i</sub>, c<sub>i</sub>]</code><em> denotes that rain water can flow from cell </em><code>(r<sub>i</sub>, c<sub>i</sub>)</code><em> to <strong>both</strong> the Pacific and Atlantic oceans</em>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/08/waterflow-grid.jpg" style="width: 400px; height: 400px;" />
<pre>
<strong>Input:</strong> heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
<strong>Output:</strong> [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
<strong>Explanation:</strong> The following cells can flow to the Pacific and Atlantic oceans, as shown below:
[0,4]: [0,4] -&gt; Pacific Ocean 
&nbsp;      [0,4] -&gt; Atlantic Ocean
[1,3]: [1,3] -&gt; [0,3] -&gt; Pacific Ocean 
&nbsp;      [1,3] -&gt; [1,4] -&gt; Atlantic Ocean
[1,4]: [1,4] -&gt; [1,3] -&gt; [0,3] -&gt; Pacific Ocean 
&nbsp;      [1,4] -&gt; Atlantic Ocean
[2,2]: [2,2] -&gt; [1,2] -&gt; [0,2] -&gt; Pacific Ocean 
&nbsp;      [2,2] -&gt; [2,3] -&gt; [2,4] -&gt; Atlantic Ocean
[3,0]: [3,0] -&gt; Pacific Ocean 
&nbsp;      [3,0] -&gt; [4,0] -&gt; Atlantic Ocean
[3,1]: [3,1] -&gt; [3,0] -&gt; Pacific Ocean 
&nbsp;      [3,1] -&gt; [4,1] -&gt; Atlantic Ocean
[4,0]: [4,0] -&gt; Pacific Ocean 
       [4,0] -&gt; Atlantic Ocean
Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> heights = [[1]]
<strong>Output:</strong> [[0,0]]
<strong>Explanation:</strong> The water can flow from the only cell to the Pacific and Atlantic oceans.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>m == heights.length</code></li>
	<li><code>n == heights[r].length</code></li>
	<li><code>1 &lt;= m, n &lt;= 200</code></li>
	<li><code>0 &lt;= heights[r][c] &lt;= 10<sup>5</sup></code></li>
</ul>

class Solution {
public:
struct pair_hash {
    template <class T1, class T2>
    size_t operator() (const pair<T1, T2> &pair) const {
        auto hash1 = hash<T1>{}(pair.first);
        auto hash2 = hash<T2>{}(pair.second);
        return hash1 ^ hash2;
    }
};

void bfs(vector<vector<int>> &heights, unordered_map<pair<int, int>, bool, pair_hash> &visited, queue<pair<int, int>> &q) {
    vector<vector<int>> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    while (!q.empty()) {
        auto [i, j] = q.front();
        q.pop();
        for (auto dir : dirs) {
            int new_i = i + dir[0];
            int new_j = j + dir[1];
            if (new_i < 0 || new_i >= heights.size() || new_j < 0 || new_j >= heights[0].size()) {
                continue;
            }
            if (visited[{new_i, new_j}]) {
                continue;
            }
            if (heights[new_i][new_j] >= heights[i][j]) {
                visited[{new_i, new_j}] = true;
                q.push({new_i, new_j});
            }
        }
    }
}

vector<vector<int>> pacificAtlantic(vector<vector<int>> &heights) {
    int m = heights.size(), n = heights[0].size();
    unordered_map<pair<int, int>, bool, pair_hash> visited_pacific, visited_atlantic;
    queue<pair<int, int>> q_pacific, q_atlantic;
    vector<vector<int>> results;

    for (int i = 0; i < m; ++i) {
        visited_pacific[{i, 0}] = true;
        visited_atlantic[{i, n-1}] = true;
        q_pacific.push({i, 0});
        q_atlantic.push({i, n-1});
    }

    for (int j = 0; j < n; ++j) {
        visited_pacific[{0, j}] = true;
        visited_atlantic[{m-1, j}] = true;
        q_pacific.push({0, j});
        q_atlantic.push({m-1, j});
    }

    bfs(heights, visited_pacific, q_pacific);
    bfs(heights, visited_atlantic, q_atlantic);

    for (int i = 0; i < m; ++i) {
        for (int j = 0; j < n; ++j) {
            if (visited_pacific[{i, j}] && visited_atlantic[{i, j}]) {
                results.push_back({i, j});
            }
        }
    }

    sort(results.begin(), results.end());
    return results;
}
// struct pair_hash {
//     template <class T1, class T2>
//     size_t operator() (const pair<T1, T2> &pair) const {
//         auto hash1 = hash<T1>{}(pair.first);
//         auto hash2 = hash<T2>{}(pair.second);
//         return hash1 ^ hash2;
//     }
// };

// void find_all_reachable_cells(vector<vector<int>> &heights, unordered_set<pair<int, int>, pair_hash> &visited_search, int i, int j) {
//     vector<vector<int>> dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
//     for (auto dir : dirs) {
//         int new_i = i + dir[0];
//         int new_j = j + dir[1];
//         if (new_i < 0 || new_i >= heights.size() || new_j < 0 || new_j >= heights[0].size()) {
//             continue;
//         }
//         if (visited_search.find({new_i, new_j}) != visited_search.end()) {
//             continue;
//         }
//         if (heights[new_i][new_j] >= heights[i][j]) {
//             visited_search.insert({new_i, new_j});
//             find_all_reachable_cells(heights, visited_search, new_i, new_j);
//         }
//     }
// }

// vector<vector<int>> pacificAtlantic(vector<vector<int>> &heights) {
//     unordered_set<pair<int, int>, pair_hash> visited_pacific_search;
//     unordered_set<pair<int, int>, pair_hash> visited_atlantic_search;
//     vector<vector<int>> results;

//     // initialize pacific set
//     for (int i = 0; i < heights.size(); i++) {
//         for (int j = 0; j < heights[0].size(); j++) {
//             if (i == 0 || j == 0) {
//                 visited_pacific_search.insert({i, j});
//                 find_all_reachable_cells(heights, visited_pacific_search, i, j);
//             }
//             if (i == heights.size() - 1 || j == heights[0].size() - 1) {
//                 visited_atlantic_search.insert({i, j});
//                 find_all_reachable_cells(heights, visited_atlantic_search, i, j);
//             }
//         }
//     }

//     // Find the intersection
//     for (const auto& elem : visited_pacific_search) {
//         if (visited_atlantic_search.find(elem) != visited_atlantic_search.end()) {
//             results.push_back({elem.first, elem.second});
//         }
//     }

//     return results;
// }
};
46000