Conversation
問題文 ``` You are painting a fence of n posts with k different colors. You must paint the posts following these rules: Every post must be painted exactly one color. There cannot be three or more consecutive posts with the same color. Given the two integers n and k, return the number of ways you can paint the fence. Example 1: Input: n = 3, k = 2 Output: 6 Explanation: All the possibilities are shown. Note that painting all the posts red or all the posts green is invalid because there cannot be three posts in a row with the same color. Example 2: Input: n = 1, k = 1 Output: 1 Example 3: Input: n = 7, k = 2 Output: 42 Constraints: 1 <= n <= 50 1 <= k <= 10^5 The testcases are generated such that the answer is in the range [0, 2^31 - 1] for the given n and k. ```
|
|
||
| #### STEP1以外の手法と感想 | ||
| - DPはメモ化再帰と類似する(既に分かっている情報を記録しておいてそれを利用することで計算量を削減できる)という話を聞いたことがあるが、これを往々するイメージがなかった。ただ、1次元DP的な発想がないとこの手法はできない。 | ||
| - メモ化再帰としてはmapを作ってメモを更新していく方法があるが、pythonのrlu_cacheのようなデータ構造を作成して使う方法もあるようだ |
There was a problem hiding this comment.
ありがとうございます。
意味を思い出して間違えないようにします
| - DPはメモ化再帰と類似する(既に分かっている情報を記録しておいてそれを利用することで計算量を削減できる)という話を聞いたことがあるが、これを往々するイメージがなかった。ただ、1次元DP的な発想がないとこの手法はできない。 | ||
| - メモ化再帰としてはmapを作ってメモを更新していく方法があるが、pythonのrlu_cacheのようなデータ構造を作成して使う方法もあるようだ | ||
| - rlu_cacheは実装が大変そうなので2周目の宿題にする | ||
| - メモ化しないと2^nぐらいの計算量になるので n = 50が上限だと、メモ化は必須 |
There was a problem hiding this comment.
これは 1.6^n くらいになります。
robHelper(100) と呼ぶと、robHelper(99) と robHelper(98) が呼ばれて、再帰的に木ができあがりますね。一番下の葉は robHelper(0) と robHelper(1) です。
robHelper(0) と robHelper(1) は、かかった計算コストの請求書を呼び出した人たちに提出します。呼び出した人たちは請求書をホチキスで止めて呼び出し元に送ります。
最後、請求書の束ができあがりますね。請求書は何枚あって、ホチキスは何回止められましたか。
https://discord.com/channels/1084280443945353267/1322513618217996338/1343221128096780420
There was a problem hiding this comment.
ありがとうございます
hroc135/leetcode#33 (comment)
こちらで理解しました。丁度この関数の計算量(請求書の数)がフィボナッチ数列の第N項になるので、黄金比を使って1.6^Nのオーダーになるということですね。
ホッチキスの数は、いわゆる「トーナメントにおける総試合数」と同じで、請求書の数 - 1になるということですね
| std::vector<int> paint_ways(n); | ||
| paint_ways[0] = k; | ||
| paint_ways[1] = k * k; | ||
| for (int fence_index = 2; fence_index < n; fence_index++) { |
There was a problem hiding this comment.
個人的にはループ内の処理が3行くらいなら、単にiでループした方が理解しやすいと思います。逆に、ループの中身が長いあるいは複数あるような状況では、処理を追う際に迷子にならないように修飾語をつけたindex等の命名にします。
There was a problem hiding this comment.
ありがとうございます。indexも1つで、コードも短いのでiで良い気がしてきました。
| int paint_ways_of_same_color = 0; | ||
| int paint_ways_of_different_color = k; | ||
| for (int fence_index = 1; fence_index < n; fence_index++) { | ||
| int temp = paint_ways_of_different_color; |
There was a problem hiding this comment.
好みの範囲だと思いますが、187-191は読み解きにくかったです。i番目のフェンスを1つ前と同じ色で塗るか、違う色で塗るか、i番目のフェンスの塗り方の総計、1つ前のフェンスの塗り方の総計、2つ前のフェンスの塗り方の総計を使って書いた方が意図が伝わる気がします。
There was a problem hiding this comment.
ありがとうございます。共通因数で括ってしまいましたが、おっしゃる通り足し算にした方が意味がとりやすいですね
class Solution {
public:
int countWays(int n, int k) {
if (n == 0 || k == 0) {
return 0;
}
int paint_ways_of_same_color = 0;
int paint_ways_of_different_color = k;
for (int fence_index = 1; fence_index < n; fence_index++) {
int temp = paint_ways_of_different_color;
paint_ways_of_different_color = (paint_ways_of_same_color) * (k - 1)
+ (paint_ways_of_different_color) * (k - 1);
paint_ways_of_same_color = temp;
}
return paint_ways_of_same_color + paint_ways_of_different_color;
}
};
This Problem
Paint Fence
Next Problem
Longest Increasing Subsequence
問題文