-
Notifications
You must be signed in to change notification settings - Fork 0
276. Paint Fence #16
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
Open
katsukii
wants to merge
1
commit into
main
Choose a base branch
from
276_paint
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+187
−0
Open
276. Paint Fence #16
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
## Problem | ||
https://leetcode.com/problems/paint-fence/ | ||
|
||
## Step 1 | ||
5分程度答えを見ずに考えて、手が止まるまでやってみる。 | ||
何も思いつかなければ、答えを見て解く。ただし、コードを書くときは答えを見ないこと。 | ||
動かないコードも記録する。 | ||
正解したら一旦OK。思考過程もメモする。 | ||
|
||
### Approach | ||
* 手書きでフェンスを書きながら考えてみたが、思いつかなかったので解法を見た | ||
|
||
```java | ||
class Solution { | ||
public int numWays(int n, int k) { | ||
if (n == 0) return 0; | ||
if (n == 1) return k; | ||
|
||
// Ways for n = 2 | ||
int sameAsPrevious = k; | ||
int diffFromPrevious = k * (k - 1); | ||
int total = sameAsPrevious + diffFromPrevious; | ||
|
||
// Ways for n >= 3 | ||
for (int i = 3; i <= n; i++) { | ||
sameAsPrevious = diffFromPrevious; | ||
diffFromPrevious = total * (k - 1); | ||
total = sameAsPrevious + diffFromPrevious; | ||
} | ||
return total; | ||
} | ||
} | ||
``` | ||
|
||
## Step 2 | ||
他の方が描いたコードを見て、参考にしてコードを書き直してみる。 | ||
参考にしたコードのリンクは貼っておく。 | ||
読みやすいことを意識する。 | ||
他の解法も考えみる。 | ||
|
||
### 参考しにしたPR | ||
* https://github.com/Mike0121/LeetCode/pull/48 | ||
* https://github.com/kazukiii/leetcode/pull/31 | ||
* https://github.com/Ryotaro25/leetcode_first60/pull/33 | ||
* https://github.com/TORUS0818/leetcode/pull/32 | ||
* https://github.com/Yoshiki-Iwasa/Arai60/pull/44 | ||
|
||
* 行列 | ||
|
||
### 解法1. 動的計画法 | ||
時間計算量: O(n) | ||
空間計算量: O(1) | ||
|
||
* フェンスを一本ずつ走査する中で、一つ前までのフェンスの塗り方の組み合わせの総数を保存しておき利用する動的計画法 | ||
|
||
```java | ||
class Solution { | ||
public int numWays(int n, int k) { | ||
if (n == 0) return 0; | ||
if (n == 1) return k; | ||
|
||
// Ways for n = 2 | ||
int sameAsPrevious = k; | ||
int diffFromPrevious = k * (k - 1); | ||
int total = sameAsPrevious + diffFromPrevious; | ||
|
||
// Ways for n >= 3 | ||
for (int i = 3; i <= n; i++) { | ||
sameAsPrevious = diffFromPrevious; | ||
diffFromPrevious = total * (k - 1); | ||
total = sameAsPrevious + diffFromPrevious; | ||
} | ||
return total; | ||
} | ||
} | ||
``` | ||
|
||
### 解法2. 動的計画法(配列を使用) | ||
時間計算量: O(n) | ||
空間計算量: O(n) | ||
|
||
* 配列を使用するため解法1より空間計算量が多い | ||
* 個人的にはDPとしてはこちらの方が馴染みやすさがあった | ||
|
||
```java | ||
class Solution { | ||
public int numWays(int n, int k) { | ||
if (n == 0) return 0; | ||
if (n == 1) return k; | ||
|
||
int[] ways = new int[n + 1]; | ||
ways[1] = k; // Can choose from k colors | ||
ways[2] = k * k; | ||
|
||
// Ways for n >= 3 | ||
// 1. Same as prev: ways[i - 2] * (k - 1) | ||
// 2. Different from prev: ways[i - 1] * (k - 1) | ||
// Thus, Total = (ways[i - 1] + ways[i - 2]) * (k - 1) | ||
for (int i = 3; i <= n; i++) { | ||
ways[i] = (ways[i - 2] + ways[i - 1]) * (k - 1); | ||
} | ||
|
||
return ways[n]; | ||
} | ||
} | ||
``` | ||
|
||
### 解法3. 行列累乗 | ||
時間計算量: O(log n) | ||
空間計算量: O(1) (2×2の固定の行列サイズ) | ||
|
||
* 動的計画法(DP)で用いた再帰関係を、行列で表現して解くというコンセプト | ||
* DPの計算量O(n)に対し、行列累乗であればO(log n)で解けるため指数的に高速化できる | ||
|
||
|
||
```java | ||
class Solution { | ||
public int numWays(int n, int k) { | ||
if (n == 0) return 0; | ||
if (n == 1) return k; | ||
if (n == 2) return k * k; | ||
|
||
long[][] base = { | ||
{k - 1, k - 1}, | ||
{1, 0} | ||
}; | ||
|
||
long[][] result = matrixPower(base, n - 2); | ||
|
||
long dp1 = k * k; // dp(2) | ||
long dp2 = k; // dp(1) | ||
|
||
return (int)(result[0][0] * dp1 + result[0][1] * dp2); | ||
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 でいいのではないでしょうか。意味が分かっているならばともかく「とりあえず long」みたいなのはあまり好まれないでしょう。 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. お手本を写経しながら練習していたため深く考えずこうなっておりました。以後気をつけます。 |
||
} | ||
|
||
private long[][] matrixPower(long[][] base, int exp) { | ||
long[][] result = {{1, 0}, {0, 1}}; | ||
|
||
while (exp > 0) { | ||
if (exp % 2 == 1) { | ||
result = multiplyMatrix(result, base); | ||
} | ||
base = multiplyMatrix(base, base); | ||
exp /= 2; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
private long[][] multiplyMatrix(long[][] a, long[][] b) { | ||
long[][] c = new long[2][2]; | ||
for (int i = 0; i < 2; i++) { | ||
for (int j = 0; j < 2; j++) { | ||
c[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j]; | ||
} | ||
} | ||
return c; | ||
} | ||
} | ||
``` | ||
|
||
|
||
## Step 3 | ||
今度は、時間を測りながら、もう一回書く。 | ||
アクセプトされたら消すを3回連続できたら問題はOK。 | ||
|
||
```java | ||
class Solution { | ||
public int numWays(int n, int k) { | ||
if (n == 0) return 0; | ||
if (n == 1) return k; | ||
|
||
// Ways for n = 2 | ||
int sameAsPrevious = k; | ||
int diffFromPrevious = k * (k - 1); | ||
int total = sameAsPrevious + diffFromPrevious; | ||
|
||
// Ways for n >= 3 | ||
for (int i = 3; i <= n; i++) { | ||
sameAsPrevious = diffFromPrevious; | ||
diffFromPrevious = total * (k - 1); | ||
total = sameAsPrevious + diffFromPrevious; | ||
} | ||
return total; | ||
} | ||
} | ||
``` |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
この dp、dynamic programming でさえない気がしますね。