Skip to content

322. Coin Change#44

Open
Ryotaro25 wants to merge 5 commits into
mainfrom
problem40
Open

322. Coin Change#44
Ryotaro25 wants to merge 5 commits into
mainfrom
problem40

Conversation

@Ryotaro25
Copy link
Copy Markdown
Owner

問題へのリンク
https://leetcode.com/problems/coin-change/description/

問題文(プレミアムの場合)

備考

次に解く問題の予告
35. Search Insert Position

フォルダ構成
LeetCodeの問題ごとにフォルダを作成します。
フォルダ内は、step1.cpp、step2.cpp、step3.cpp、bfs.cppとmemo.mdとなります。

memo.md内に各ステップで感じたことを追記します。

Comment thread 322.CoinChange/bfs.cpp
int coinChange(vector<int>& coins, int target_amount) {
vector<bool> visited(target_amount + 1, false);

queue<NumCoinsAndAmount> num_coins_and_amount;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私は、個人的には pair<int, int> のほうが読みやすいかなと思いますが、これは色々意見があるでしょう。

Comment thread 322.CoinChange/bfs.cpp
continue;
}

int next_amount = amount + coin_val; // ここでintのrangeを超える場合がある
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

このコメントは、どちらかというと、「このため上で引き算によって先に判別した」という意図でしょうか。

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
レビューありがとうございます。
見返してみて、いきなりこのコメントが出てくると意味が分からないですね。
bfs_step2.cppにコメントを追加しました。

Comment thread 322.CoinChange/step3.cpp

num_min_coins[0] = 0;
for (int amount = 1; amount <= target_amount; amount++) {
for (const auto& coin_val : coins) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

私は、これは単に 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.

64bit CPUだとintが4バイトでreferenceが8バイトなのでintの方が良いのかなと思います。コンパイラ側で最適化などあるかもしれないですが。(詳しくないので変なこと言ってたら教えて下さい)

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 @hayashi-ay
レビューありがとうございます。
林さんの指摘の通りreferenceにすると追加で4バイト使用することになるので、あえてintを使わない選択はしないほうが良さそうでした。intに変更しました。

Comment thread 322.CoinChange/memo.md
https://github.com/goto-untrapped/Arai60/pull/34
https://github.com/sakupan102/arai60-practice/pull/41
## Discorなど

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

どこかでいいと思うのですが、C++ はある程度大規模になると .cc と .h に分けて書くのが普通です。

宣言と実体を分けて書く練習はどこかでしたほうがいいでしょう。
https://source.chromium.org/gn/gn/+/main:src/base/files/file.cc

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.

「メモ」
c++で用いるファイルの種類
https://qiita.com/Nabetani/items/9345f764c716dcf7c3f6
ファイル分割に関するグーグルガイド
https://google.github.io/styleguide/cppguide.html#Header_Files

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
頂いた例とグーグルガイドを見ながらファイルを分けてみました。
新たにmainディレクトリを作成し.hと.ccファイルに分割したものをgitに上げました🙇‍♂️

Comment thread 322.CoinChange/step3.cpp
int coinChange(vector<int>& coins, int target_amount) {
vector<int> num_min_coins(target_amount + 1, UNDEFINED);

num_min_coins[0] = 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

英語の語順としては、min_num_coinsの方が自然だと思います。

https://github.com/hayashi-ay/leetcode/pull/68/files#r1546152560

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

あとは、min_coinsでも十分伝わるような気がします

Comment thread 322.CoinChange/step3.cpp
}

private:
static constexpr int UNDEFINED = std::numeric_limits<int>::max();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

UNDEFINEDという定数を定義するのにすこし違和感があります。UNMAKABLEUNREACHABLEなどの方がより意図が明確になるかなと思います。

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.

確かにUNREACHABLEの方がより正確だと思いましたのでこちらを使いました!ありがとうございます。

Comment thread 322.CoinChange/step3.cpp
if (num_min_coins.back() != UNDEFINED) {
return num_min_coins.back();
}
return -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.

@nittoco
レビューありがとうございます。

If that amount of money cannot be made up by any combination of the coins, return -1.

こうあったのでそこまで考えておりませんでした。UNACHIVABLEとして定義してみました。

Comment thread 322.CoinChange/bfs.cpp
Comment on lines +30 to +31
visited[next_amount] = true;
num_coins_and_amount.push({num_coins + 1, next_amount});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

細かくて恐縮ですが、L7~8ではpushした後にvisitedを更新してますが、こちらでは逆ですね

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.

@nittoco
統一感なかったですね。指摘ありがとうございます。修正しました。

Comment thread 322.CoinChange/step2.cpp
Comment on lines +29 to +31
private:
static constexpr int UNDEFINED = std::numeric_limits<int>::max();
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これはSolutionの先頭においたほうがいいんじゃないでしょうか
C++のお作法知らないのですが、基本的に上から下に読んでそのまま理解できることが望ましいと思います

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

半分肯定ですね。プロダクションコードだと、.h, .cc に普通は分けるんです。
https://source.chromium.org/gn/gn/+/main:src/base/files/file.h シグネーチャー等。
https://source.chromium.org/gn/gn/+/main:src/base/files/file.cc 実体。
一部抜き出すと .h は下のような感じです。そして、実装は .cc に回します。

namespace base {
class File {
 public:
  File();
  void Initialize(const FilePath& path, uint32_t flags);
  void Close();
 private:
  void DoInitialize(const FilePath& path, uint32_t flags);
  File(const File&) = delete;
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

こっちが .cc 側の抜き出し。

#include "base/files/file.h"

namespace base {

void File::Initialize(const FilePath& path, uint32_t flags) {
  if (path.ReferencesParent()) {
#if defined(OS_WIN)
    ::SetLastError(ERROR_ACCESS_DENIED);
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
    errno = EACCES;
#else
#error Unsupported platform
#endif
    error_details_ = FILE_ERROR_ACCESS_DENIED;
    return;
  }
  DoInitialize(path, flags);
}

}  // namespace base

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なので本当はこれに慣れた状況にしないといけません。

元の質問に戻ると、class 定義は .h に書かれてどういうクラスであるだけが書かれているので、外からのインターフェースが前で(サイズを決めるのに必要な) private 情報は後ろに来ます。ただ、普通は実体がさらに後ろに回っているので、半分は肯定ですね。

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.

@Yoshiki-Iwasa @oda
レビューありがとうございます。

ヘッダーファイルと実態を分ける等は知りませんでした。
下記のガイドに従ってprivateを後半に持ってきておりました。
https://google.github.io/styleguide/cppguide.html#Declaration_Order

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.

前に共有したものはコンパイルできておりませんでした。
コンパイルできたものはこちらになります。
d5c48dd

Comment thread 322.CoinChange/main/step4.cc Outdated
@@ -0,0 +1,29 @@
#include "322.CoinChange/main/step4.h"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

あれ?Step4のコードはコンパイルできましたか?

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
分割してみただけで確認しておりませんでした。コンパイル自体初めてなのですがやってみます。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

たぶんコンパイルできないです。何箇所か問題があると思いますが、試してみてください。
ぱっと気になるところだと、ヘッダファイルのパス、cc側でのクラス定義の実装部分ですかね?Makefile(CMakeLists)なども軽く触ってみても良いかもしれないです。

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
長らく空いてしまい、また後回しにしてしまい申し訳ございません。
なんとかコンパイルできるところまで完了しました。
d5c48dd

これまでLeetCode用に書いてきたものと大きく異なりまだ理解が出来ておりません。
google guideには下記のように方針がございましたがもう少し初学者向けに何をどこに書くのか書かれたものがございましたら教えていただきたいです🙇‍♂️

Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly.

https://google.github.io/styleguide/cppguide.html#Self_contained_Headers

Comment thread 322.CoinChange/bfs.cpp
Comment on lines +18 to +19
// ここにvisited[amount] = true;をおくとMemory Limit Exceeded
// 各コインを探索した後でvisitedが更新されるので
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

以下は通るのですが、こういう話とは違いますか?

class Solution {
public:
  int coinChange(vector<int>& coins, int target_amount) {
    vector<bool> visited(target_amount + 1, false);

    queue<NumCoinsAndAmount> num_coins_and_amount;
    num_coins_and_amount.push({0, 0});
    // visited[0] = true;

    while (!num_coins_and_amount.empty()) {
      auto [num_coins, amount] = num_coins_and_amount.front();
      num_coins_and_amount.pop();

      if (amount == target_amount) {
        return num_coins;
      }

      // ここにvisited[amount] = true;をおくとMemory Limit Exceeded
      // 各コインを探索した後でvisitedが更新されるので
      if (visited[amount]) {
          continue;
      }
      visited[amount] = true;

      for (int coin_val : coins) {
        // target_amountを超えないか確認
        // next_amount計算時にオーバーフローが発生するのを防ぐため
        if (amount > target_amount - coin_val) {
          continue;
        }

        int next_amount = amount + coin_val;
        
        num_coins_and_amount.push({num_coins + 1, next_amount});
      }
    }

    return -1;
  }

private:
  struct NumCoinsAndAmount {
    int num_coins;
    int amount;
  };
};

@Ryotaro25
Copy link
Copy Markdown
Owner Author

Compilerの関係でディレクトリ名を変更しました。
d5c48dd

Comment thread 322_CoinChange/bfs.cpp
// ここにvisited[amount] = true;をおくとMemory Limit Exceeded
// 各コインを探索した後でvisitedが更新されるので

for (const auto& coin_val : coins) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

変数名は coin で十分伝わるように思いました。

また、参照で受けると、間接アドレッシングで値にアクセスするコードが生成される場合があり、無駄な処理が行われる場合があります。レジスターサイズと比べて大きくない値については、値型で受け取ることをお勧めいたします。

詳しくは「Effective C++ 第3版」の「20項 値渡しよりconst参照渡しを使おう」をご覧ください。 (自分は第 2 版しか読んでおらず、この項の次の項に値渡しを使うことが書かれていました。第 3 版はこの項に値渡しを使う場合のことが書かれているのではないかと予想します。)

Comment thread 322_CoinChange/step2.cpp
class Solution {
public:
int coinChange(vector<int>& coins, int target_amount) {
vector<int> num_min_coins(target_amount + 1, UNDEFINED);
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_min_coins という名前ですと、 number of minimum coins といった英語句の省略形のように見え、意味が通らないように思います。min_coins で十分意味が通り、かつシンプルになると思います。

Comment thread 322_CoinChange/bfs.cpp
continue;
}
visited[next_amount] = true;
num_coins_and_amount.push({num_coins + 1, next_amount});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

next_amount == target_amount の判定をここで行ったほうが、 push() pop() の回数が減り、処理が少し軽くなると思います。ただ、ネストが深くなるため、一長一短だとも思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants