Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions 62.UniquePaths/combination3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution {
public:
int uniquePaths(int m, int n) {
// (m - 1) + (n - 1) = m + n - 2
return combination(m + n - 2, min(m - 1, n - 1));
}

private:
int64_t combination(int64_t n, int64_t r) {
int64_t result = 1;

for (int64_t i = 1; i <= r; i++) {
result *= (n - i + 1);
result /= i;
}

return result;
}
};
18 changes: 18 additions & 0 deletions 62.UniquePaths/combitation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
int uniquePaths(int m, int n) {
// (m - 1) + (n - 1) = m + n - 2
return combination(m + n - 2, min(m - 1, n - 1));
}

private:
int combination(int n, int r) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

long long

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liquo-rice
早速の確認ありがとうございます。
処理はlong longで行っておりますが返却時にstatic_castを用いてint型にしております。
関数内でキャストはすべきでないですか?

Copy link
Copy Markdown

@liquo-rice liquo-rice Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intが必要な場面ならば、implicitにintにキャストされませんか? (おそらく警告が出ますかね。)仮にintで収まらない値を返すときはどうしたら良さそうですか?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上のuniquePathsでキャストした方が良さそうな気がします。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分なら int64_t を使うのですが、明示的な理由はありません。強いて言えば、メモリモデルによらずビット数が決まっているからでしょうか…。

long long result = 1;

for (int i = 1; i <= r; i++) {
result = result * (n - i + 1) / i;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result *= ...

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

たぶん result *= (n - i + 1) / i;にしてしまうと計算の順序が変わってしまって途中で切り捨てが発生して計算結果が変わってしまうと思います。
https://en.cppreference.com/w/cpp/language/operator_precedence

例) 7C3の場合
Step1

  1. 1 * 7 / 1 -> 7
  2. 7 * 6 / 2 -> 21
  3. 21 * 5 / 3 -> 35

Step2

  1. ① 7 / 1 -> 7 ② 1 * 7 -> 7
  2. ① 6 / 2 -> 3 ② 7 * 3 -> 21
  3. ① 5 / 3 -> 1 ② 21 * 1 -> 21
    ←ここで切り捨てが起こる

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

これ自分も一瞬読んでる時「ん?」となりました。切り捨て考慮して、
result *= (n - i + 1)
result /= i
が素直ですかね?分けたほうが読みやすい気もします

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n- i + 1で別変数定義も一瞬思いましたが、やや過剰ですかね

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hayashi-ay @nittoco
レビューありがとうございます。指摘頂いた通りで、計算結果が変わっておりテストケースによってはパスできませんでした。
2段に分けて実装しました。

}

return static_cast<int>(result);
}
};
18 changes: 18 additions & 0 deletions 62.UniquePaths/combitation2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
class Solution {
public:
int uniquePaths(int m, int n) {
// (m - 1) + (n - 1) = m + n - 2
return combination(m + n - 2, min(m - 1, n - 1));
}

private:
int combination(int n, int r) {
long long result = 1;

for (int i = 1; i <= r; i++) {
result *= (n - i + 1) / i;
}

return static_cast<int>(result);
}
};
42 changes: 42 additions & 0 deletions 62.UniquePaths/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
## ステップ1
問題を見て思いついたのは
(m + n)!/m!n!の公式を使う解法と1マスごとに、ロボットが何パターンで進むことができたのか記録する方法。
今回は後者の方法で行う。右端に辿り着いた時点でのパターン数が解答になる。

時間計算量O(m n)
空間計算量O(m n)

## ステップ2
i jを使うかrow colを使うかは好みの問題か
空間計算量を抑えることはできる

## 参考にした他の方の解法
基本的は方針は同じ
~~数学的な解き方でも解いてみる。C++に階乗やコンビネーションのライブラリはなさそう。~~
数学的な解き方でも解いてみる。C++に階乗やコンビネーションの標準ライブラリはなさそう。
https://github.com/Yoshiki-Iwasa/Arai60/pull/47
combitation.cppに実装
扱う数字の大きさを抑える方法が分からなかったのでChat GPTに相談
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Python だと標準で、Java だと BigInteger など多倍長整数を扱うことが標準でできるのですが、C++ はなかったと思います。おそらく Boost を使うなどになりますね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda
レビューありがとうございます。
調べてみましたが無かったです。LeetCodeで標準外のライブラリを使えないのは知りませんでした。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

多倍長整数クラスは一度実装してみても良いかもしれません。

combinationの第二引数には可能な限り小さい数値を渡すためにminを用いる
→たまたまLeetCodeのテストケースで通っただけのような気がする

行単位もしくは列単位でパターン数を記録することで空間計算量をO(m) もしくはO(n)に抑えることができる
ただ、コード面接でこれは思いつけそうにない
https://github.com/fhiyo/leetcode/pull/34
step4.cppに実装(写経)

## Discordなど

## 多倍長整数クラス
64bitより長い数値を扱う計算全般を広義の多倍長(multiple precision)計算

https://na-inet.jp/weblog2/2018/03/22/%E3%81%9D%E3%82%82%E3%81%9D%E3%82%82%E3%80%8C%E5%A4%9A%E5%80%8D%E9%95%B7%E3%80%8D%E3%81%A3%E3%81%A6%E3%81%A9%E3%81%86%E3%81%84%E3%81%86%E6%84%8F%E5%91%B3%EF%BC%9F/

https://zenn.dev/herumi/articles/bitint-01-cpp

Boost
https://ja.wikipedia.org/wiki/Boost_C%2B%2B%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA
cpp_int.hpp
https://github.com/boostorg/multiprecision/blob/develop/include/boost/multiprecision/cpp_int.hpp

実装は難しかったので一周してから戻ってくる
9 changes: 9 additions & 0 deletions 62.UniquePaths/recursive.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Solution {
public:
int uniquePaths(int row, int col) {
if (row == 1 || col == 1) {
return 1;
}
return uniquePaths(row - 1, col) + uniquePaths(row, col - 1);
}
};
14 changes: 14 additions & 0 deletions 62.UniquePaths/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> num_paths(m, vector<int>(n, 1));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

後から上書きするから問題ないとして、とりあえず全部に 1 を入れておくのはあまり行儀が良くない(読む人を混乱させる)ようには思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oda
左端の列と1番上の行を1で更新するように変更しました。
9cafa91

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Step5も結局コメントで補足を入れているので、個人的にはStep1の解法にコメントを足すだけでも良いのかなとも思いました。


for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
num_paths[i][j] = num_paths[i - 1][j] + num_paths[i][j - 1];
}
}

return num_paths[m - 1][n - 1];
}
};
14 changes: 14 additions & 0 deletions 62.UniquePaths/step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> num_paths(m, vector<int>(n, 1));

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
num_paths[i][j] = num_paths[i - 1][j] + num_paths[i][j - 1];
}
}

return num_paths[m - 1][n - 1];
}
};
13 changes: 13 additions & 0 deletions 62.UniquePaths/step3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> num_paths(m, vector<int>(n, 1));
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
num_paths[i][j] = num_paths[i - 1][j] + num_paths[i][j - 1];
}
}

return num_paths[m - 1][n - 1];
}
};
22 changes: 22 additions & 0 deletions 62.UniquePaths/step4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Solution {
public:
int uniquePaths(int m, int n) {
// m と n を比較して、n の方が大きい場合、m と n を交換
if (m < n) {
swap(m, n);
}

vector<int> num_paths(n, 1);

// m - 1 回ループで行を更新
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
// 左と上からの経路数を足す
num_paths[j] += num_paths[j - 1];
}
}

// 最後のセルのパス数を返す
return num_paths[n - 1];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最後の要素を返すのであれば、 num_paths.back() のほうが分かりやすいと思います。

}
};
21 changes: 21 additions & 0 deletions 62.UniquePaths/step5.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
public:
int uniquePaths(int m, int n) {
vector<vector<int>> num_paths(m, vector<int>(n));
// 到達まで1パターンの部分を初期化
for (int i = 0; i < m; i++) {
num_paths[i][0] = 1;
}
for (int j = 0; j < n; j++) {
num_paths[0][j] = 1;
}

for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
num_paths[i][j] = num_paths[i - 1][j] + num_paths[i][j - 1];
}
}

return num_paths[m - 1][n - 1];
}
};