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
97 changes: 97 additions & 0 deletions 276.PaintFence/LRU.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
class Node {
public:
int key;
int value;
Node* prev;
Node* next;
Node(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
};

class MyLRUCache {
public:
MyLRUCache(int size) : limit(size), size(0) {
sentinel = new Node(-1, -1);
sentinel->next = sentinel;
sentinel->prev = sentinel;
}

~MyLRUCache() {
delete sentinel;
}

int Get(int key) {
if (cache.find(key) == cache.end()) {
return -1;
}
Node* node = cache[key];
int value = node->value;
Remove(key);
Put(key, value);
return value;
}

void Put(int key, int value) {
if (cache.find(key) != cache.end()) {
Remove(key);
}
Node* node = new Node(key, value);
Node* head = sentinel->next;
sentinel->next = node;
node->next = head;
head->prev = node;
node->prev = sentinel;
cache[key] = node;
size++;
if (size > limit) {
Remove(sentinel->prev->key);
}
}

private:
// 今回はhashなので
unordered_map<int, Node*> cache;
int limit;
int size;
Node* sentinel;

void Remove(int key) {
Node* node = cache[key];
node->prev->next = node->next;
node->next->prev = node->prev;
cache.erase(key);
delete node;
size--;
}
};

class Solution {
public:
int numWays(int num_posts, int num_colors) {
MyLRUCache cache(1000);
Copy link

Choose a reason for hiding this comment

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

これ、num_posts が 1000 を超えたらどうなりますかね。

計算時間としては爆発しないようですね。cache サイズが2以上あれば大丈夫ですか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

@oda

レビューありがとうございます。今回の問題(自分の実装の場合)の場合は2で問題なかったです。
皆さんのを写経するだけで、cacheを1000にする理由を考えておりませんでした。

関数CountNumWaysの中で、使うのは今いる箇所から-1したところと-2した箇所の情報だけです。

int same_pattern_ways = CountNumWays(num_posts - 2, num_colors, cache);
int different_pattern_ways = CountNumWays(num_posts - 1, num_colors, cache);

PutしていてもCacheのサイズ2つ確保しておけば必要な分は消されないので問題なさそうです。
自分の解法だとnum_postsの数はあまり影響しないので、使えてなさそうですね。。。

cache.Put(1, num_colors);
cache.Put(2, num_colors * num_colors);

return CountNumWays(num_posts, num_colors, cache);
}

private:
int CountNumWays(int num_posts, int num_colors, MyLRUCache& cache) {
if (num_posts == 1) {
return num_colors;
}
if (num_posts == 2) {
return num_colors * num_colors;
}

int colors_ways = cache.Get(num_posts);
if (colors_ways != -1) {
return colors_ways;
}

int same_pattern_ways = CountNumWays(num_posts - 2, num_colors, cache);
int different_pattern_ways = CountNumWays(num_posts - 1, num_colors, cache);
colors_ways = (num_colors - 1) * (same_pattern_ways + different_pattern_ways);
cache.Put(num_posts, colors_ways);
return colors_ways;
}
};
97 changes: 97 additions & 0 deletions 276.PaintFence/LRU_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
class Node {
public:
int key;
int value;
Node* prev;
Node* next;
Node(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
};

class MyLRUCache {
public:
MyLRUCache(int size) : limit(size), size(0) {
sentinel = new Node(-1, -1);
sentinel->next = sentinel;
sentinel->prev = sentinel;
}

~MyLRUCache() {
delete sentinel;
}

int Get(int key) {
if (cache.find(key) == cache.end()) {
return -1;
}
Node* node = cache[key];
int value = node->value;
Remove(key);
Put(key, value);
return value;
}

void Put(int key, int value) {
if (cache.find(key) != cache.end()) {
Remove(key);
}
Node* node = new Node(key, value);
Node* head = sentinel->next;
sentinel->next = node;
node->next = head;
head->prev = node;
node->prev = sentinel;
cache[key] = node;
size++;
if (size > limit) {
Remove(sentinel->prev->key);
}
}

private:
// 今回はhashなので
unordered_map<int, Node*> cache;
int limit;
int size;
Node* sentinel;

void Remove(int key) {
Node* node = cache[key];
node->prev->next = node->next;
node->next->prev = node->prev;
cache.erase(key);
delete node;
size--;
}
};

class Solution {
public:
int numWays(int num_posts, int num_colors) {
MyLRUCache cache(1000);
cache.Put(1, num_colors);
cache.Put(2, num_colors * num_colors);

return CountNumWays(num_posts, num_colors, cache);
}

private:
int CountNumWays(int num_posts, int num_colors, MyLRUCache& cache) {
if (num_posts == 1) {
return num_colors;
}
if (num_posts == 2) {
return num_colors * num_colors;
}

int colors_ways = cache.Get(num_posts);
if (colors_ways != -1) {
return colors_ways;
}

int same_pattern_ways = CountNumWays(num_posts - 2, num_colors, cache);
int different_pattern_ways = CountNumWays(num_posts - 1, num_colors, cache);
colors_ways = (num_colors - 1) * (same_pattern_ways + different_pattern_ways);
cache.Put(num_posts, colors_ways);
return colors_ways;
}
};
23 changes: 23 additions & 0 deletions 276.PaintFence/bottom_up_step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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;
}
vector<int> paint_ways_so_far(n + 1);
paint_ways_so_far[1] = k;
paint_ways_so_far[2] = k * k;
for (int i = 3; i <= n; i++) {
int same_paint = paint_ways_so_far[i - 2] * (k - 1);
int diff_paint = paint_ways_so_far[i - 1] * (k - 1);
paint_ways_so_far[i] = same_paint + diff_paint;
}
return paint_ways_so_far.back();
}
};
24 changes: 24 additions & 0 deletions 276.PaintFence/bottom_up_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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;
}
int two_previous_way = k;
int one_previous_way = k * k;
for (int i = 2; i < n; i++) {
int same_paint = two_previous_way * (k - 1);
int diff_paint = one_previous_way * (k - 1);

two_previous_way = one_previous_way;
one_previous_way = same_paint + diff_paint;
}
return one_previous_way;
}
};
72 changes: 72 additions & 0 deletions 276.PaintFence/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## ステップ1
色のパターンはkもしくはk-1
>There cannot be three or more consecutive posts with the same color.
なので、同じ色が2色まではkそれ以外はk-1種類
postsの数が0色、1色や2色の場合もあるので考慮する
3色目以降は、1つ前と2つ前の状態を考慮して色を選ぶ必要がある

時間
O(n)

空間
O(1)

## ステップ2
・変数名変更
waysをつけることで明示的に方法(複数形)があると伝える

・2つ前、1つ前のの情報を保持するのにvectorを使う必要はない
past_two_colors_waysの要素は2つなのでvectorに入れる必要はない

・0の場合の特別処理を削除
回答に使っているnum_waysを0で初期化しているため

## 他の解法
__・フィボナッチ数列を使うことで時間計算量 O(log n)で解くことができる__
  参照 : https://github.com/TORUS0818/leetcode/pull/32/commits/50d9a5f46afbe969f08a116f412232e540ca159d
  参照 :https://github.com/goto-untrapped/Arai60/pull/44/commits/b0b76df73f7b0b3c15b5ae4aef0066cfe611eb1b

__・DPに、bottom-upとtop-downの2種類が存在する__
  アルゴリズムイントロダクション第二巻に乗っているので関連する章を読む(8/30)
  参照 : https://github.com/TORUS0818/leetcode/pull/32/commits/50d9a5f46afbe969f08a116f412232e540ca159d

以下アルゴリズムイントロダクション第二巻を読んで追記(8/31)
*トップダウン式*
さっくりとした理解
nこの要素があったとしてn-1、n-2...と範囲を狭めながら再帰的に部分問題を処理する方式
top_down.cppにて実装。
ループの中で、直前2つが同じ色をしているものと異なる色を再帰的に呼び出している
時間
O(n)

空間
O(n)

*ボトムアップ式*
ざっくりとした理解
トップダウンとは反対に0, 1, 2,....nと要素を昇順に処理していく方式
step1~3がボトムアップ方式となっている(実装後に用語知りました。。。)

__・メモ化しながら再帰で解く方法も存在する__
https://github.com/Yoshiki-Iwasa/Arai60/pull/44/commits/3a55cc495ac31c94e38e1b39f40055ed9d261f18

単純に再帰で解くと時間計算量がO(2^n)となるので要素数に比例して実行時間が凄くかかる
このトレードオフとしてメモ化がある(状態を保持しておくためのメモリを要するため)
メモ化しながら存在するというよりは、メモ化しないと時間計算量が爆発的になる


__・LRUを使っている__
なぜ?
https://github.com/goto-untrapped/Arai60/pull/44/commits/b0b76df73f7b0b3c15b5ae4aef0066cfe611eb1b
https://discord.com/channels/1084280443945353267/1226508154833993788/1253725840114454598
>LRU cacheというのがよく聞かれるのってなんでなんだろう(どういう出題意図なんだろう)
>話の導入がしやすく、助け舟も出しやすく、書けるからですね。

なるほど、つまり常識として(使える選択肢として)理解しておくべきか
キャパシティを設定しておいて、超えた場合は自動で削除する
これによって無駄にリソースを使わない

実装の参考
https://github.com/goto-untrapped/Arai60/pull/44/commits/b0b76df73f7b0b3c15b5ae4aef0066cfe611eb1b
https://github.com/sakupan102/arai60-practice/pull/31
LRU.cppに実装
22 changes: 22 additions & 0 deletions 276.PaintFence/recursion_step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class Solution {
public:
int numWays(int n, int k) {
if (n == 0) {
return 0;
}
return PaintWays(n, k);
}

private:
int PaintWays(int num_fence, int num_color) {
if (num_fence == 1) {
return num_color;
}
if (num_fence == 2) {
return num_color * num_color;
}
int diff_paint = (num_color - 1) * PaintWays(num_fence - 1, num_color);
int same_paint = (num_color - 1) * PaintWays(num_fence - 2, num_color);
return diff_paint + same_paint;
}
};
28 changes: 28 additions & 0 deletions 276.PaintFence/recursion_step2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class Solution {
public:
int numWays(int n, int k) {
if (n == 0) {
return 0;
}
// initialize with impossible value
vector<int> paint_ways_so_far(n + 1, -1);
return PaintWays(paint_ways_so_far, n, k);
}

private:
int PaintWays(vector<int>& paint_ways_so_far, int num_fence, int num_color) {
if (paint_ways_so_far[num_fence] != -1) {
return paint_ways_so_far[num_fence];
}
if (num_fence == 1) {
return num_color;
}
if (num_fence == 2) {
return num_color * num_color;
}
int diff_paint = (num_color - 1) * PaintWays(paint_ways_so_far, num_fence - 1, num_color);
int same_paint = (num_color - 1) * PaintWays(paint_ways_so_far, num_fence - 2, num_color);
paint_ways_so_far[num_fence] = diff_paint + same_paint;
return diff_paint + same_paint;
}
};
27 changes: 27 additions & 0 deletions 276.PaintFence/step1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Solution {
public:
int numWays(int num_posts, int num_colors) {
if (num_posts == 0) {
return 0;
}
if (num_posts == 1) {
return num_colors;
}
if (num_posts == 2) {
return num_colors * num_colors;
}

vector<int> past_two_colors(2);
Copy link

Choose a reason for hiding this comment

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

vector<int> はヒープ領域にメモリを確保する都合上、スタック領域だけで処理を完結させた場合と比べ、処理が重くなる場合があります。 step2 のように、スタック領域だけで処理を完結することができるのであれば、そちらを選んだほうが良いと思います。

past_two_colors[0] = num_colors;
past_two_colors[1] = num_colors * num_colors;

int colors = 0;
for (int i = 2; i < num_posts; i++) {
colors = (num_colors - 1) * (past_two_colors[0] + past_two_colors[1]);
past_two_colors[0] = past_two_colors[1];
past_two_colors[1] = colors;
}

return colors;
}
};
Loading