diff --git a/problems/373.find-k-pairs-with-smallest-sums/memo.md b/problems/373.find-k-pairs-with-smallest-sums/memo.md new file mode 100644 index 0000000..c53308c --- /dev/null +++ b/problems/373.find-k-pairs-with-smallest-sums/memo.md @@ -0,0 +1,65 @@ +## step1 +- 一旦愚直に解く方法を考える +- 選び方はnums1.length*nums2.length通りで、合計でsortする場合O(NlogN) +- 10^5*10^5 + 10^10log10 が大体 10^10(1+3.1)で1秒間に10^8回計算できる場合は410秒でTLE + +- 改善案 + - 取り方的にkを超えるindexは無視して良さそう + - その場合でも4.1秒でTLEしそう + - 全てのパターンを列挙するので1秒掛かる + - 一旦実装したが、Memory Limit Exceededになった + +```py +def kSmallestPairs( + self, nums1: List[int], nums2: List[int], k: int +) -> List[List[int]]: + sum_combibations = [] + for num1 in nums1[:k]: + for num2 in nums2[:k]: + sum_combibations.append([num1 + num2, [num1, num2]]) + + sum_combibations.sort() + + return [combination for [_sum, combination] in sum_combibations[:k]] +``` + +- 愚直に列挙するやり方は無理そうなので前から数字を見ていき、取り方のルールを整理する[nums1のindex, nums2のindex]とする + - 1番目の答え: [0,0] + - 2番目の答えの候補:[0,1] or [1,0] + - 3番目の答えの候補 + - 2番目の答えが[0,1]: [1,0] or [0,2] + - 2番目の答えが[1,0]: [0,1] or [2,1] + - 4番目の答えの候補 + - 3番目の答えが[1,0]2番目の答えが[0,1]: [1,1] or [2,0] or [0,2] +- n=>n+1の時にindexが戻ることがあるのが嫌すぎる +- これまでの履歴から答えの候補になるindexは決まっているのでその中から都度選んでいくのがいい? +- 合計とindexをheapqに入れておいてpopしていく作戦 + - 最初の提出では重複して同じペアをカウントしてしまったが、その後修正したらAcceptedになった + +## step2 +- 次のindexの存在確認とリストへの追加のところがごちゃごちゃしてるように感じるし、繰り返しているように見える +- 時間計算量はO(klogk),空間計算量はO(k)だと思う +- 他の人のコードを読む +- https://github.com/Hiroto-Iizuka/coding_practice/pull/10/files + - 十分に理解できておらず、swapさせるうまみみたいなのがわからなかった +- https://github.com/mamo3gr/arai60/pull/10/files + - `空間計算量を求めたあと、実際のどの程度のメモリを使用するか推定されましたでしょうか?もししていないのであれば、推定することをお勧めいたします。推定するには、時間計算量の式に最大サイズを代入し、 1 要素当たりのサイズを掛けてみてください。`. `sys.getsizeof`など勉強になった + - 自分で言うところのsum_heapは最小値候補なので、`candidates`とした方が良いように思った + - 自分の命名だと意味が付け加えられていないように感じた + - `enqueue_if_valid_and_not_visited`確かにわかりやすい +- https://github.com/Yuto729/LeetCode_arai60/pull/16/files + - `C++: 約 1~10 億ステップ/秒` + - `CPU の周波数が数 GHz 程度であるため、IPC(Instructions per Cycle)を 1 と仮定すると、1 秒間に数十億命令を実行できます。C++ はオーバーヘッドが小さく、1 ステップを数命令〜数十命令で実行できるため、おおよそ 1~10 億ステップ/秒が目安となります。` + - `Python: 約 100 万~1000 万ステップ/秒` + - `インタープリタ方式によるオーバーヘッドが大きく、C++ の約 100 倍遅いため、この程度が目安になります。` + +- 一旦、`enqueue_if_valid_and_not_visited`の対応を行う +- 個人的には`visited_pair_set`にすると候補というニュアンスが薄れる気がする +- candidateにaddしたという意味で`processed`を使っていたがなんでも`process`なので`added`とかにしておく + +## step3 +- 関数の中でnum1_index, num2_indexと書くと、読む量が増えてくどく感じたのでi,jにした +- n1_indexでも十分伝わると思ったので少し省略した +- データ構造を名前に含めがちな癖があるので`added_indice_set`とせず`added_indice`とした + + diff --git a/problems/373.find-k-pairs-with-smallest-sums/step1.py b/problems/373.find-k-pairs-with-smallest-sums/step1.py new file mode 100644 index 0000000..a8ad6ba --- /dev/null +++ b/problems/373.find-k-pairs-with-smallest-sums/step1.py @@ -0,0 +1,51 @@ +# +# @lc app=leetcode id=373 lang=python3 +# +# [373] Find K Pairs with Smallest Sums +# + +# @lc code=start +import heapq + + +class Solution: + def kSmallestPairs( + self, nums1: List[int], nums2: List[int], k: int + ) -> List[List[int]]: + sum_heap = [[nums1[0] + nums2[0], [0, 0]]] + + k_small_pairs = [] + processed_pair_set = set((0, 0)) + + while len(k_small_pairs) < k: + [_, num1_num2_index] = heapq.heappop(sum_heap) + [num1_index, num2_index] = num1_num2_index + k_small_pairs.append([nums1[num1_index], nums2[num2_index]]) + if ( + num1_index + 1 < len(nums1) + and (num1_index + 1, num2_index) not in processed_pair_set + ): + heapq.heappush( + sum_heap, + [ + nums1[num1_index + 1] + nums2[num2_index], + [num1_index + 1, num2_index], + ], + ) + processed_pair_set.add((num1_index + 1, num2_index)) + if ( + num2_index + 1 < len(nums2) + and (num1_index, num2_index + 1) not in processed_pair_set + ): + heapq.heappush( + sum_heap, + [ + nums1[num1_index] + nums2[num2_index + 1], + [num1_index, num2_index + 1], + ], + ) + processed_pair_set.add((num1_index, num2_index + 1)) + return k_small_pairs + + +# @lc code=end diff --git a/problems/373.find-k-pairs-with-smallest-sums/step2.py b/problems/373.find-k-pairs-with-smallest-sums/step2.py new file mode 100644 index 0000000..ae4225c --- /dev/null +++ b/problems/373.find-k-pairs-with-smallest-sums/step2.py @@ -0,0 +1,45 @@ +# +# @lc app=leetcode id=373 lang=python3 +# +# [373] Find K Pairs with Smallest Sums +# + +# @lc code=start +import heapq + + +class Solution: + def kSmallestPairs( + self, nums1: List[int], nums2: List[int], k: int + ) -> List[List[int]]: + candidate = [] + + k_small_pairs = [] + added_pair_set = set((0, 0)) + + def enqueue_if_valid_and_not_added(num1_index, num2_index): + if num1_index >= len(nums1) or num2_index >= len(nums2): + return + if (num1_index, num2_index) in added_pair_set: + return + heapq.heappush( + candidate, + [ + nums1[num1_index] + nums2[num2_index], + [num1_index, num2_index], + ], + ) + added_pair_set.add((num1_index, num2_index)) + + enqueue_if_valid_and_not_added(0, 0) + + while len(k_small_pairs) < k: + [_, num1_num2_index] = heapq.heappop(candidate) + [num1_index, num2_index] = num1_num2_index + k_small_pairs.append([nums1[num1_index], nums2[num2_index]]) + enqueue_if_valid_and_not_added(num1_index + 1, num2_index) + enqueue_if_valid_and_not_added(num1_index, num2_index + 1) + return k_small_pairs + + +# @lc code=end diff --git a/problems/373.find-k-pairs-with-smallest-sums/step3.py b/problems/373.find-k-pairs-with-smallest-sums/step3.py new file mode 100644 index 0000000..d66d991 --- /dev/null +++ b/problems/373.find-k-pairs-with-smallest-sums/step3.py @@ -0,0 +1,40 @@ +# +# @lc app=leetcode id=373 lang=python3 +# +# [373] Find K Pairs with Smallest Sums +# + +# @lc code=start + +import heapq + + +class Solution: + def kSmallestPairs( + self, nums1: List[int], nums2: List[int], k: int + ) -> List[List[int]]: + candidate = [] + added_indice = set() + + smallest_pairs = [] + + def enqueue_if_valid_and_not_added(i, j): + if i >= len(nums1) or j >= len(nums2): + return + if (i, j) in added_indice: + return + heapq.heappush(candidate, [nums1[i] + nums2[j], [i, j]]) + added_indice.add((i, j)) + + enqueue_if_valid_and_not_added(0, 0) + + while len(smallest_pairs) < k: + _sum, [n1_index, n2_index] = heapq.heappop(candidate) + smallest_pairs.append([nums1[n1_index], nums2[n2_index]]) + enqueue_if_valid_and_not_added(n1_index + 1, n2_index) + enqueue_if_valid_and_not_added(n1_index, n2_index + 1) + + return smallest_pairs + + +# @lc code=end