diff --git a/373/step1.cpp b/373/step1.cpp new file mode 100644 index 0000000..433abaa --- /dev/null +++ b/373/step1.cpp @@ -0,0 +1,84 @@ +/* +1時間以上 + +Time: O( log(max(nums1,nums2)) * n log(m) ) +Space: O( k ) +n = nums1.size() +m = nums2.size() + +nums1,nums2のすべてのペアを検証すると時間が足りないので、それを補う方法を考える。 +ペアの数がk個以下となるような値Lを二分探索で探す。 +合計値がL以下のペアをpriority_queueにいれて最小k個のペアを探す。 +・二分探索での桁溢れ +・同値のペアが大量に作成される +などの問題を都度修正したら過アンリ時間がかかってしまったがかろうじてパスはしたものの +色々と筋悪な回答なのでstep2以降で修正する。 + +*/ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + long long int left = nums1.front() + nums2.front(); + long long int right = nums1.back() + nums2.back() + 1; + long long int mid; + while (left + 1 < right) { + mid = left + (right - left) / 2; + int count = count_less_pairs(nums1, nums2, mid, k); + if (count >= k) { + right = mid; + } else { + left = mid; + } + } + + priority_queue sum_pairs; + for (int num1 : nums1) { + for (int num2 : nums2) { + if (num1 + num2 > right) { + break; + } + sum_pairs.push({num1 + num2, {num1, num2}}); + if (sum_pairs.size() > k) { + sum_pairs.pop(); + if (num1 + num2 == sum_pairs.top().sum) { + break; + } + } + } + } + + vector> answer; + while (!sum_pairs.empty()) { + answer.push_back(sum_pairs.top().pair); + sum_pairs.pop(); + } + return answer; + } + private: + + struct SumPair { + int sum; + vector pair; + + bool operator< (const SumPair& other) const { + return sum < other.sum; + } + }; + + int count_less_pairs(vector nums1, vectornums2, int num, int limit) { + int count_sum = 0; + for (int num1 : nums1) { + auto it = upper_bound(nums2.begin(), nums2.end(), num - num1); + if (it == nums2.end()) { + count_sum += nums2.size(); + if (count_sum > limit) { + return limit; + } + continue; + } + int count = it - nums2.begin(); + count_sum += count; + } + return count_sum; + } +}; diff --git a/373/step1_fix.cpp b/373/step1_fix.cpp new file mode 100644 index 0000000..354cb8a --- /dev/null +++ b/373/step1_fix.cpp @@ -0,0 +1,66 @@ +/* +step1 after review +*/ + +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + int64_t left = nums1.front() + nums2.front(); + int64_t right = nums1.back() + nums2.back() + 1; + while (left < right) { + int64_t mid = left + (right - left) / 2; + int count = count_less_pairs(nums1, nums2, mid, k); + if (count >= k) { + right = mid; + } else { + left = mid + 1; + } + } + + int max_sum = left; + priority_queue sum_pairs; + for (int num1 : nums1) { + for (int num2 : nums2) { + if (num1 + num2 > max_sum) { + break; + } + sum_pairs.push({num1 + num2, {num1, num2}}); + if (sum_pairs.size() > k) { + sum_pairs.pop(); + if (num1 + num2 == sum_pairs.top().sum) { + break; + } + } + } + } + + vector> answer; + while (!sum_pairs.empty()) { + answer.push_back(sum_pairs.top().pair); + sum_pairs.pop(); + } + return answer; + } + private: + + struct SumPair { + int sum; + vector pair; + + bool operator< (const SumPair& other) const { + return sum < other.sum; + } + }; + + int count_less_pairs(vector nums1, vectornums2, int num, int limit) { + int num_pairs = 0; + for (int num1 : nums1) { + auto it = upper_bound(nums2.begin(), nums2.end(), num - num1); + num_pairs += distance(nums2.begin(), it); + if (num_pairs > limit) { + return limit; + } + } + return num_pairs; + } +}; diff --git a/373/step2.cpp b/373/step2.cpp new file mode 100644 index 0000000..bf5808d --- /dev/null +++ b/373/step2.cpp @@ -0,0 +1,41 @@ +/* +他の方の回答を参考にやり直したもの +nums1,nums2のインデックスのペアをダイクストラ法っぽく処理していく +Time: O(k logk) +Space: O(k) +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue, greater> sum_index; + set> visited; + vector> answer; + sum_index.push({nums1[0] + nums2[0], {0, 0}}); + while (answer.size() < k) { + int i = sum_index.top().index[0]; + int j = sum_index.top().index[1]; + sum_index.pop(); + answer.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j})) { + sum_index.push({nums1[i + 1] + nums2[j], {i + 1, j}}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1})) { + sum_index.push({nums1[i] + nums2[j + 1], {i, j + 1}}); + visited.insert({i, j + 1}); + } + } + + return answer; + } + + private: + struct SumIndex { + int sum; + vector index; + + bool operator> (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step2_2.cpp b/373/step2_2.cpp new file mode 100644 index 0000000..417f650 --- /dev/null +++ b/373/step2_2.cpp @@ -0,0 +1,38 @@ +/* +step2 after review +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue sum_index; + set> visited; + vector> answer; + sum_index.push({nums1[0] + nums2[0], {0, 0}}); + while (answer.size() < k) { + int i = sum_index.top().index[0]; + int j = sum_index.top().index[1]; + sum_index.pop(); + answer.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j})) { + sum_index.push({nums1[i + 1] + nums2[j], {i + 1, j}}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1})) { + sum_index.push({nums1[i] + nums2[j + 1], {i, j + 1}}); + visited.insert({i, j + 1}); + } + } + + return answer; + } + + private: + struct SumIndex { + int sum; + vector index; + + bool operator< (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step3_1.cpp b/373/step3_1.cpp new file mode 100644 index 0000000..f23345f --- /dev/null +++ b/373/step3_1.cpp @@ -0,0 +1,27 @@ +/* +struct不使用パターン +*/ +class Solution { +public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue, vector>, greater>> sum_index; + set> visited; + vector> answer; + sum_index.push({0, 0, 0}); + while (answer.size() < k && sum_index.size() > 0) { + int i = sum_index.top()[1]; + int j = sum_index.top()[2]; + sum_index.pop(); + answer.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j})) { + sum_index.push({nums1[i + 1] + nums2[j], i + 1, j}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1})) { + sum_index.push({nums1[i] + nums2[j + 1], i, j + 1}); + visited.insert({i, j + 1}); + } + } + return answer; + } +}; diff --git a/373/step3_2.cpp b/373/step3_2.cpp new file mode 100644 index 0000000..349ee5d --- /dev/null +++ b/373/step3_2.cpp @@ -0,0 +1,39 @@ +/* +structを使用したパターン +priority_queueやstructの宣言で若干冗長に感じるが、こちらのほうが読みやすい +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue, greater> sum_index; + set> visited; + vector> minimum_k_pairs; + sum_index.push({nums1[0] + nums2[0], 0, 0}); + while (minimum_k_pairs.size() < k && sum_index.size() > 0) { + int i = sum_index.top().num1_index; + int j = sum_index.top().num2_index; + sum_index.pop(); + minimum_k_pairs.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j})) { + sum_index.push({nums1[i + 1] + nums2[j], i+1, j}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1})) { + sum_index.push({nums1[i] + nums2[j + 1], i, j + 1}); + visited.insert({i, j + 1}); + } + } + return minimum_k_pairs; + } + + private: + struct SumIndex { + int sum; + int num1_index; + int num2_index; + + bool operator> (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step3_3.cpp b/373/step3_3.cpp new file mode 100644 index 0000000..208286a --- /dev/null +++ b/373/step3_3.cpp @@ -0,0 +1,39 @@ +/* +{i + 1, j}と{i, j + 1}をpriority_queueにいれる時に、{i + 1, j - 1}と{i - 1, j + 1}がvisitedに含まれているかを確認して効率化するパターン +条件判定部分が無駄に複雑なので関数化したいが、いい感じにできないのでそのままにしている +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue, greater> sum_index; + vector> minimum_k_pair; + set> visited; + sum_index.push({nums1[0] + nums2[0], 0, 0}); + while (minimum_k_pair.size() < k && sum_index.size() > 0) { + int i = sum_index.top().num1_index; + int j = sum_index.top().num2_index; + sum_index.pop(); + minimum_k_pair.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j}) && (j == 0 || visited.contains({i + 1, j - 1}))) { + sum_index.push({nums1[i + 1] + nums2[j], i + 1, j}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1}) && (i == 0 || visited.contains({i - 1, j + 1}))) { + sum_index.push({nums1[i] + nums2[j + 1], i, j + 1}); + visited.insert({i, j + 1}); + } + } + return minimum_k_pair; + } + + private: + struct SumIndex { + int sum; + int num1_index; + int num2_index; + + bool operator> (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step3_3_fix.cpp b/373/step3_3_fix.cpp new file mode 100644 index 0000000..208286a --- /dev/null +++ b/373/step3_3_fix.cpp @@ -0,0 +1,39 @@ +/* +{i + 1, j}と{i, j + 1}をpriority_queueにいれる時に、{i + 1, j - 1}と{i - 1, j + 1}がvisitedに含まれているかを確認して効率化するパターン +条件判定部分が無駄に複雑なので関数化したいが、いい感じにできないのでそのままにしている +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue, greater> sum_index; + vector> minimum_k_pair; + set> visited; + sum_index.push({nums1[0] + nums2[0], 0, 0}); + while (minimum_k_pair.size() < k && sum_index.size() > 0) { + int i = sum_index.top().num1_index; + int j = sum_index.top().num2_index; + sum_index.pop(); + minimum_k_pair.push_back({nums1[i], nums2[j]}); + if (i + 1 < nums1.size() && !visited.contains({i + 1, j}) && (j == 0 || visited.contains({i + 1, j - 1}))) { + sum_index.push({nums1[i + 1] + nums2[j], i + 1, j}); + visited.insert({i + 1, j}); + } + if (j + 1 < nums2.size() && !visited.contains({i, j + 1}) && (i == 0 || visited.contains({i - 1, j + 1}))) { + sum_index.push({nums1[i] + nums2[j + 1], i, j + 1}); + visited.insert({i, j + 1}); + } + } + return minimum_k_pair; + } + + private: + struct SumIndex { + int sum; + int num1_index; + int num2_index; + + bool operator> (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step3_3_no_set.cpp b/373/step3_3_no_set.cpp new file mode 100644 index 0000000..d0a70c4 --- /dev/null +++ b/373/step3_3_no_set.cpp @@ -0,0 +1,37 @@ +/* +https://github.com/colorbox/leetcode/pull/25#discussion_r1759948961 +https://github.com/ryoooooory/LeetCode/pull/17/files#diff-1288c579d3218c63304f49c9c9a04568c3305bef4a38145045001270d6f6b5c6 +を参考としたコード +右・下移動をpriority_queueから取り出すごとに行うが、下移動は一番上にいるときのみに限って行うことで、衝突を避ける。 +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue k_pair_candidates; + vector> minimum_k_pair; + k_pair_candidates.push({nums1[0] + nums2[0], 0, 0}); + while (minimum_k_pair.size() < k && k_pair_candidates.size() > 0) { + auto [sum, i, j] = k_pair_candidates.top(); + k_pair_candidates.pop(); + minimum_k_pair.push_back({nums1[i], nums2[j]}); + if (j == 0 && i + 1 < nums1.size()) { + k_pair_candidates.push({nums1[i + 1] + nums2[j], i + 1, j}); + } + if (j + 1 < nums2.size()) { + k_pair_candidates.push({nums1[i] + nums2[j + 1], i, j + 1}); + } + } + return minimum_k_pair; + } + + private: + struct SumIndex { + int sum; + int num1_index; + int num2_index; + + bool operator< (const SumIndex& other) const { + return sum > other.sum; + } + }; +}; diff --git a/373/step3_3_no_set_2.cpp b/373/step3_3_no_set_2.cpp new file mode 100644 index 0000000..b1ef83e --- /dev/null +++ b/373/step3_3_no_set_2.cpp @@ -0,0 +1,36 @@ +/* +https://github.com/colorbox/leetcode/pull/25#discussion_r1766166184 +https://github.com/seal-azarashi/leetcode/pull/10/files#diff-1738c7b2b125c2158f2d6502754f18d9c179c559fca2b92d22d1a6bfc16ce429 +上記を参考にしたコード +一旦1行目の候補をすべてpriority_queueにpushし、その後取り出した候補を一つずつ降下させることで重複なく候補を取り出す。 +*/ +class Solution { + public: + vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { + priority_queue k_pair_candidates; + vector> minimum_k_pair; + for (int i = 0; i < nums1.size(); i++) { + k_pair_candidates.push({nums1[i] + nums2[0], i, 0}); + } + while (minimum_k_pair.size() < k && k_pair_candidates.size() > 0) { + auto [sum, i, j] = k_pair_candidates.top(); + k_pair_candidates.pop(); + minimum_k_pair.push_back({nums1[i], nums2[j]}); + if (j + 1 < nums2.size()) { + k_pair_candidates.push({nums1[i] + nums2[j + 1], i, j + 1}); + } + } + return minimum_k_pair; + } + + private: + struct SumIndex { + int sum; + int num1_index; + int num2_index; + + bool operator< (const SumIndex& other) const { + return sum > other.sum; + } + }; +};