-
Notifications
You must be signed in to change notification settings - Fork 0
695 Max Area of Island #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| # step1 | ||
| 前回の問題とほとんど並列に進みそう。 | ||
| グリッドを走査して最初に未発見の島を見つけたところからカウントをする。BFSとDFSどちらでもできそうだがいったんBFSで書く。 | ||
|
|
||
| 前回はseen_gridを用意していたが、gridをコピーして書き換えながら探索したかチェックする。 | ||
|
|
||
| ```cpp:step1.cpp | ||
| ``` | ||
|
|
||
| # step2 | ||
|
|
||
| ## 他の人のコードを読む | ||
| - early returnする書き方も自然で取り入れたい。 https://github.com/akmhmgc/arai60/pull/15/files#r2347265871 | ||
|
|
||
| ## Union Find | ||
| 前回スルーしてしまったのでやってみる。 | ||
| - odaさんの解説 https://discord.com/channels/1084280443945353267/1183683738635346001/1197738650998415500 | ||
| - 参考にした実装 https://algo-logic.info/union-find-tree/ | ||
|
|
||
| 時間計算量は(m*n*α(m*n)) (alphaは逆アッカーマン関数) | ||
|
|
||
| ```cpp:step2_union_find.cpp | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class DisjointUnionSet { | ||
| private: | ||
| const int n; | ||
| std::vector<int> parents; | ||
| std::vector<int> size; | ||
| int find_root(int x) { | ||
| if (parents[x] == x) { | ||
| return x; | ||
| } | ||
| // path compression | ||
| return parents[x] = find_root(parents[x]); | ||
| } | ||
|
|
||
| public: | ||
| DisjointUnionSet(int n) : n(n), parents(n), size(n, 1) { | ||
| for (auto i : std::views::iota(0, n)) { | ||
| parents[i] = i; | ||
| } | ||
| } | ||
| bool same(int x, int y) { return find_root(x) == find_root(y); } | ||
| // return root of components of x and y after unite | ||
| int unite(int x, int y) { | ||
| x = find_root(x); | ||
| y = find_root(y); | ||
| if (x == y) { | ||
| return x; | ||
| } | ||
| // union by size | ||
| if (size[x] < size[y]) { | ||
| std::swap(x, y); | ||
| } | ||
| parents[y] = x; | ||
| size[x] += size[y]; | ||
| return x; | ||
| } | ||
| int size_of_components(int x) { return size[find_root(x)]; } | ||
| }; | ||
|
|
||
| class Solution { | ||
| public: | ||
| int maxAreaOfIsland(const std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
| int num_rows = grid.size(); | ||
| int num_cols = grid[0].size(); | ||
|
|
||
| DisjointUnionSet connected_component_manager(num_rows * num_cols); | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| if ((row + 1 < num_rows) && grid[row][col] == grid[row + 1][col]) { | ||
| connected_component_manager.unite(row * num_cols + col, (row + 1) * num_cols + col); | ||
| } | ||
| if ((col + 1 < num_cols) && grid[row][col] == grid[row][col + 1]) { | ||
| connected_component_manager.unite(row * num_cols + col, row * num_cols + col + 1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int result = 0; | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| if (grid[row][col] == 0) { | ||
| continue; | ||
| } | ||
| result = | ||
| std::max(result, connected_component_manager.size_of_components(row * num_cols + col)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| }; | ||
|
|
||
| ``` | ||
|
|
||
| ## 自分用メモ | ||
| - 再帰を非再帰で書き直す https://qiita.com/KowerKoint/items/870ea9ef7a39f3fe4ce3 | ||
| - CPU命令の時間比較 http://ithare.com/infographics-operation-costs-in-cpu-clock-cycles/ | ||
|
|
||
|
|
||
| ```cpp:step2.cpp | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| /** | ||
| * 入力が空のグリッドの場合は-1を返す | ||
| */ | ||
| int maxAreaOfIsland(const std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
| num_rows = static_cast<int>(grid.size()); | ||
| num_cols = static_cast<int>(grid[0].size()); | ||
| unvisited_islands = grid; | ||
|
|
||
| int result = 0; | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| result = std::max(result, traverse_and_count_area(row, col)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private: | ||
| // 本来ならconstをつけてコンストラクタで初期化したほうがよい | ||
| int num_rows; | ||
| int num_cols; | ||
| std::vector<std::vector<int>> unvisited_islands; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 状態をメンバー変数として持たせた場合、複数のスレッドから呼び出したときに競合が起きたり、複数回呼び出したときに結果が意図しない結果になるといった問題が起こりえます。引数で引き回すことをおすすめします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. たしかにSolutionクラスに複数スレッドからアクセスする場合引数に渡す必要がありますね… 本問題の場合最初にmaxAreaOfIslandが受け取るgrid自体は複数スレッドで共有しうると思うのですが、Solutionクラス自体は各スレッドで構築するシチュエーションが多いんじゃないかと思っていました。 複数スレッドをを想定する場合、メンバー変数にはconstなもののみ置いたほうがよいのでしょうか。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 複数スレッドを想定する場合、 const なメンバー変数は置いても安全なことが多いと思います。ただし、 const std::shared_ptr 等、その変数の先にオブジェクトがある場合は注意が必要です。そのオブジェクトを書き換える必要がある場合は、ロックを取ってから読み書きする、といった実装にする必要があると思います。 ロックを取る際は、ロックを取りたい範囲や条件にもよりますが、 std::mutex や std::lock_guard などが使われると思います。 |
||
|
|
||
| int traverse_and_count_area(int row, int col) { | ||
| if (!(0 <= row && row < num_rows && 0 <= col && col < num_cols)) { | ||
| return 0; | ||
| } | ||
| if (!unvisited_islands[row][col]) { | ||
| return 0; | ||
| } | ||
| // 訪れた | ||
| unvisited_islands[row][col] = 0; | ||
| return 1 + traverse_and_count_area(row + 1, col) + traverse_and_count_area(row, col + 1) + | ||
| traverse_and_count_area(row - 1, col) + traverse_and_count_area(row, col - 1); | ||
| } | ||
| }; | ||
|
|
||
| ``` | ||
|
|
||
|
|
||
| # step3 | ||
| ```cpp:step3.cpp | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| int maxAreaOfIsland(std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
|
|
||
| num_rows = grid.size(); | ||
| num_cols = grid[0].size(); | ||
| unvisited_islands = grid; | ||
|
|
||
| int result = 0; | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| result = std::max(result, traverse_and_count_area(row, col)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private: | ||
| int num_rows; | ||
| int num_cols; | ||
| std::vector<std::vector<int>> unvisited_islands; | ||
| int traverse_and_count_area(int row, int col) { | ||
| if (!(0 <= row && row < num_rows && 0 <= col && col < num_cols)) { | ||
| return 0; | ||
| } | ||
| if (unvisited_islands[row][col] == 0) { | ||
| return 0; | ||
| } | ||
|
|
||
| unvisited_islands[row][col] = 0; | ||
| return 1 + traverse_and_count_area(row + 1, col) + traverse_and_count_area(row, col + 1) + | ||
| traverse_and_count_area(row - 1, col) + traverse_and_count_area(row, col - 1); | ||
| } | ||
| }; | ||
|
|
||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #include <array> | ||
| #include <queue> | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| // 入力が不正な場合は-1を返す | ||
| int maxAreaOfIsland(const std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
| num_rows = static_cast<int>(grid.size()); | ||
| num_cols = static_cast<int>(grid[0].size()); | ||
|
|
||
| int max_area_of_islands = 0; | ||
| // 1はまだ探索していない島、0は海または探索済みの島 | ||
| std::vector<std::vector<int>> unvisited_islands = grid; | ||
| for (const auto row : std::views::iota(0, num_rows)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 自分は ranged-for 文でプリミティブ型には const を付けない派です。チームの平均的な書き方に合わせることをおすすめします。 |
||
| for (const auto col : std::views::iota(0, num_cols)) { | ||
| if (unvisited_islands[row][col]) { | ||
| max_area_of_islands = | ||
| std::max(max_area_of_islands, measure_islands(row, col, unvisited_islands)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return max_area_of_islands; | ||
| } | ||
|
|
||
| private: | ||
| int num_rows; | ||
| int num_cols; | ||
| const std::array<std::pair<int, int>, 4> direction = {{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 所属するチームによって変わると思いますが、Googleのコーディング規約だと、定数名の頭にkを付けるらしいです。 また、定数定義するときは、C++11以降だとconstexprが使えます。 |
||
|
|
||
| bool is_effective_grid(const int row, const int col) const { | ||
| return 0 <= row && row < num_rows && 0 <= col && col < num_cols; | ||
| } | ||
| // 島の連結成分の面積を計算し訪問済みにする | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. メンバー関数のあいだに空行を空けることをおすすめします。 |
||
| int measure_islands(const int start_row, const int start_col, | ||
| std::vector<std::vector<int>>& unvisited_islands) { | ||
| int island_area = 0; | ||
| std::queue<std::pair<int, int>> visiting; | ||
| // BFS | ||
| unvisited_islands[start_row][start_col] = 0; | ||
| visiting.emplace(start_row, start_col); | ||
| ++island_area; | ||
| while (!visiting.empty()) { | ||
| const auto [row, col] = visiting.front(); | ||
| visiting.pop(); | ||
| for (const auto [delta_row, delta_col] : direction) { | ||
| const auto next_row = row + delta_row; | ||
| const auto next_col = col + delta_col; | ||
| if (is_effective_grid(next_row, next_col) && unvisited_islands[next_row][next_col]) { | ||
| unvisited_islands[next_row][next_col] = 0; | ||
| visiting.emplace(next_row, next_col); | ||
| ++island_area; | ||
| } | ||
| } | ||
| } | ||
| return island_area; | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class Solution { | ||
| public: | ||
| /** | ||
| * 入力が空のグリッドの場合は-1を返す | ||
| */ | ||
| int maxAreaOfIsland(const std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
| num_rows = static_cast<int>(grid.size()); | ||
| num_cols = static_cast<int>(grid[0].size()); | ||
| unvisited_islands = grid; | ||
|
|
||
| int result = 0; | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| result = std::max(result, traverse_and_count_area(row, col)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private: | ||
| // 本来ならconstをつけてコンストラクタで初期化したほうがよい | ||
| int num_rows; | ||
| int num_cols; | ||
| std::vector<std::vector<int>> unvisited_islands; | ||
|
|
||
| int traverse_and_count_area(int row, int col) { | ||
| if (!(0 <= row && row < num_rows && 0 <= col && col < num_cols)) { | ||
| return 0; | ||
| } | ||
| if (!unvisited_islands[row][col]) { | ||
| return 0; | ||
| } | ||
| // 訪れた | ||
| unvisited_islands[row][col] = 0; | ||
| return 1 + traverse_and_count_area(row + 1, col) + traverse_and_count_area(row, col + 1) + | ||
| traverse_and_count_area(row - 1, col) + traverse_and_count_area(row, col - 1); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #include <ranges> | ||
| #include <vector> | ||
|
|
||
| class DisjointUnionSet { | ||
| private: | ||
| const int n; | ||
| std::vector<int> parents; | ||
| std::vector<int> size; | ||
| int find_root(int x) { | ||
| if (parents[x] == x) { | ||
| return x; | ||
| } | ||
| // path compression | ||
| return parents[x] = find_root(parents[x]); | ||
| } | ||
|
|
||
| public: | ||
| DisjointUnionSet(int n) : n(n), parents(n), size(n, 1) { | ||
| for (auto i : std::views::iota(0, n)) { | ||
| parents[i] = i; | ||
| } | ||
| } | ||
| bool same(int x, int y) { return find_root(x) == find_root(y); } | ||
| // return root of components of x and y after unite | ||
| int unite(int x, int y) { | ||
| x = find_root(x); | ||
| y = find_root(y); | ||
| if (x == y) { | ||
| return x; | ||
| } | ||
| // union by size | ||
| if (size[x] < size[y]) { | ||
| std::swap(x, y); | ||
| } | ||
| parents[y] = x; | ||
| size[x] += size[y]; | ||
| return x; | ||
| } | ||
| int size_of_components(int x) { return size[find_root(x)]; } | ||
| }; | ||
|
|
||
| class Solution { | ||
| public: | ||
| int maxAreaOfIsland(const std::vector<std::vector<int>>& grid) { | ||
| if (grid.empty() || grid[0].empty()) { | ||
| return -1; | ||
| } | ||
| int num_rows = grid.size(); | ||
| int num_cols = grid[0].size(); | ||
|
|
||
| DisjointUnionSet connected_component_manager(num_rows * num_cols); | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| if ((row + 1 < num_rows) && grid[row][col] == grid[row + 1][col]) { | ||
| connected_component_manager.unite(row * num_cols + col, (row + 1) * num_cols + col); | ||
| } | ||
| if ((col + 1 < num_cols) && grid[row][col] == grid[row][col + 1]) { | ||
| connected_component_manager.unite(row * num_cols + col, row * num_cols + col + 1); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int result = 0; | ||
| for (auto row : std::views::iota(0, num_rows)) { | ||
| for (auto col : std::views::iota(0, num_cols)) { | ||
| if (grid[row][col] == 0) { | ||
| continue; | ||
| } | ||
| result = | ||
| std::max(result, connected_component_manager.size_of_components(row * num_cols + col)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manager という単語はよく見かけるのですが、この場合はどれくらい情報があるのが微妙に感じます。 connected_components はいかがでしょうか?