diff --git a/Algorithms/Problem_Solving/Meet_in_the_Middle/Combinatorial_Selection.cpp b/Algorithms/Problem_Solving/Meet_in_the_Middle/Combinatorial_Selection.cpp new file mode 100644 index 0000000000..d2e5432593 --- /dev/null +++ b/Algorithms/Problem_Solving/Meet_in_the_Middle/Combinatorial_Selection.cpp @@ -0,0 +1,142 @@ + +/* +--------------------------------------------------------- +Algorithm: Meet in the Middle — Combinatorial Selection Problem +--------------------------------------------------------- +Problem: +You are given `n` items, each with a cost and value. +Find the **maximum total value** you can achieve by selecting +a subset of items whose **total cost ≤ budget**. + +Constraints: +- n ≤ 40 → too large for brute-force 2^n +- cost[i], value[i] ≤ 1e9 + +Optimization: +Use "Meet in the Middle" to reduce complexity from O(2^n) +to O(2^(n/2)) by dividing the items into two halves and combining +their possible subsets efficiently. + +--------------------------------------------------------- +Example: +Input: + n = 5, budget = 10 + cost = [4, 3, 5, 8, 2] + value = [6, 4, 5, 10, 3] +Output: + 12 +Explanation: + Pick items with total cost = 9 (4 + 3 + 2) + and total value = 6 + 4 + 3 = 13 + (≤ 10 → maximum achievable) +--------------------------------------------------------- +*/ + +#include +using namespace std; + +// ----------------------------------------------------- +// Helper: Generate all subsets with (cost, value) pairs +// ----------------------------------------------------- +void generateSubsets(const vector>& items, vector>& subsets) { + int n = items.size(); + int total = 1 << n; // 2^n possible subsets + + for (int mask = 0; mask < total; ++mask) { + long long totalCost = 0, totalValue = 0; + for (int i = 0; i < n; ++i) { + if (mask & (1 << i)) { + totalCost += items[i].first; + totalValue += items[i].second; + } + } + subsets.push_back({totalCost, totalValue}); + } +} + +// ----------------------------------------------------- +// Function: Compute maximum total value under budget +// ----------------------------------------------------- +long long maxValueUnderBudget(vector>& items, int budget) { + int n = items.size(); + int mid = n / 2; + + // Split into two halves + vector> leftItems(items.begin(), items.begin() + mid); + vector> rightItems(items.begin() + mid, items.end()); + + // Generate all subset pairs (cost, value) + vector> leftSubsets, rightSubsets; + generateSubsets(leftItems, leftSubsets); + generateSubsets(rightItems, rightSubsets); + + // Sort right subsets by cost + sort(rightSubsets.begin(), rightSubsets.end()); + + // Optimize rightSubsets: remove dominated pairs + // (Keep only subsets with strictly increasing values) + vector> optimized; + long long maxValue = 0; + for (auto [c, v] : rightSubsets) { + if (v > maxValue) { + optimized.push_back({c, v}); + maxValue = v; + } + } + rightSubsets = optimized; + + long long answer = 0; + + // For each subset on left, binary search best fit on right + for (auto [costL, valL] : leftSubsets) { + if (costL > budget) continue; + + long long remaining = budget - costL; + + // Binary search for the most value we can get within remaining budget + int idx = upper_bound(rightSubsets.begin(), rightSubsets.end(), make_pair(remaining, LLONG_MAX)) - rightSubsets.begin() - 1; + + if (idx >= 0) { + long long valR = rightSubsets[idx].second; + answer = max(answer, valL + valR); + } + } + + return answer; +} + +// ----------------------------------------------------- +// Driver Code (Test) +// ----------------------------------------------------- +int main() { + vector> items = { + {4, 6}, {3, 4}, {5, 5}, {8, 10}, {2, 3} + }; + int budget = 10; + + cout << "Items (cost, value): "; + for (auto &it : items) cout << "(" << it.first << "," << it.second << ") "; + cout << "\nBudget: " << budget << endl; + + long long result = maxValueUnderBudget(items, budget); + cout << "Maximum achievable value under budget = " << result << endl; + + return 0; +} + +/* +--------------------------------------------------------- +🧠 Explanation: + +We split items into two halves: +Left = [{4,6}, {3,4}] → subsets: + []: (0,0), [4]: (4,6), [3]: (3,4), [4,3]: (7,10) + +Right = [{5,5}, {8,10}, {2,3}] → subsets: + []:(0,0), [5]:(5,5), [8]:(8,10), [2]:(2,3), + [5,8]:(13,15), [5,2]:(7,8), [8,2]:(10,13), [5,8,2]:(15,18) + +After filtering dominated pairs in right half and combining both halves, +the best achievable total value ≤ budget(10) = 13. +--------------------------------------------------------- +*/ diff --git a/Algorithms/Problem_Solving/Meet_in_the_Middle/Max_Product_Subset.cpp b/Algorithms/Problem_Solving/Meet_in_the_Middle/Max_Product_Subset.cpp new file mode 100644 index 0000000000..fbb3926d34 --- /dev/null +++ b/Algorithms/Problem_Solving/Meet_in_the_Middle/Max_Product_Subset.cpp @@ -0,0 +1,122 @@ + +/* +--------------------------------------------------------- +Algorithm: Meet in the Middle — Maximum Product Subset +--------------------------------------------------------- +Problem: +Given an array of integers, find the **maximum product** that can +be obtained from any subset of the array (non-empty subset). + +Naive approach: +- Try all 2^N subsets → O(2^N) +- For N > 30, becomes computationally infeasible + +Optimization using Meet in the Middle: +- Split array into two halves +- Generate all subset products of both halves +- Combine them efficiently to find the global maximum + +Key idea: +We only need to store all subset products and combine them +systematically (similar to subset-sum but using product operation). + +--------------------------------------------------------- +Example: +Input: arr = [2, -3, 4, -1] +Output: 24 +Explanation: subset [2, -3, 4] → 2 * (-3) * 4 = -24, +but subset [2, 4, -1, -3] → product = 24 (maximum) +--------------------------------------------------------- +*/ + +#include +using namespace std; + +// ----------------------------------------------------- +// Helper function: generate all subset products +// ----------------------------------------------------- +void generateSubsetProducts(const vector& nums, vector& subsetProducts) { + int n = nums.size(); + int totalSubsets = 1 << n; // 2^n possible subsets + + for (int mask = 1; mask < totalSubsets; ++mask) { // start from 1 to skip empty subset + long long product = 1; + for (int i = 0; i < n; ++i) { + if (mask & (1 << i)) + product *= nums[i]; + } + subsetProducts.push_back(product); + } +} + +// ----------------------------------------------------- +// Function to compute maximum subset product +// ----------------------------------------------------- +long long maxSubsetProduct(vector& arr) { + int n = arr.size(); + int mid = n / 2; + + // Split into two halves + vector leftArr(arr.begin(), arr.begin() + mid); + vector rightArr(arr.begin() + mid, arr.end()); + + // Generate subset products + vector leftProducts, rightProducts; + generateSubsetProducts(leftArr, leftProducts); + generateSubsetProducts(rightArr, rightProducts); + + // Sort for efficient pairing (not strictly needed here, but useful for extensions) + sort(leftProducts.begin(), leftProducts.end()); + sort(rightProducts.begin(), rightProducts.end()); + + long long maxProduct = LLONG_MIN; + + // Combine both halves + for (auto leftProd : leftProducts) { + for (auto rightProd : rightProducts) { + maxProduct = max(maxProduct, leftProd * rightProd); + } + } + + // Also consider individual subset products (in case best subset is within one half) + for (auto val : leftProducts) maxProduct = max(maxProduct, val); + for (auto val : rightProducts) maxProduct = max(maxProduct, val); + + return maxProduct; +} + +// ----------------------------------------------------- +// Driver Code +// ----------------------------------------------------- +int main() { + vector arr = {2, -3, 4, -1}; + + cout << "Array: "; + for (int num : arr) cout << num << " "; + cout << "\n"; + + long long result = maxSubsetProduct(arr); + cout << "Maximum product among all subsets = " << result << endl; + + return 0; +} + +/* +--------------------------------------------------------- +🧠 Explanation: + +arr = [2, -3, 4, -1] +Left half = [2, -3] +Right half = [4, -1] + +Left products = {2, -3, -6} +Right products = {4, -1, -4} + +Now combine: +(2*4)=8, (2*-1)=-2, (2*-4)=-8, +(-3*4)=-12, (-3*-1)=3, (-3*-4)=12, +(-6*4)=-24, (-6*-1)=6, (-6*-4)=24 ← ✅ max + +Hence, Maximum = 24 +--------------------------------------------------------- +*/ diff --git a/Algorithms/Problem_Solving/Meet_in_the_Middle/Subset_Sum_Variant.cpp b/Algorithms/Problem_Solving/Meet_in_the_Middle/Subset_Sum_Variant.cpp new file mode 100644 index 0000000000..a0a4022fbf --- /dev/null +++ b/Algorithms/Problem_Solving/Meet_in_the_Middle/Subset_Sum_Variant.cpp @@ -0,0 +1,115 @@ + +/* +--------------------------------------------------------- +Algorithm: Meet in the Middle — Subset Sum Variant +--------------------------------------------------------- +Problem: +Given an array of integers and a target value, count how many +subsets have a total sum ≤ target. + +Traditional brute force approach: +- Generate all subsets → O(2^N) +- Sum each subset → inefficient for large N (N > 40) + +Optimization (Meet in the Middle): +- Split array into two halves +- Compute all possible subset sums of each half +- Sort one half and for each sum in the other half, use binary + search to count how many combinations give total ≤ target. + +Time Complexity: +- O(2^(N/2) * log(2^(N/2))) ≈ O(N * 2^(N/2)) +- Works efficiently for N ≤ 40 + +--------------------------------------------------------- +Example: +Input: arr = [3, 1, 2, 5, 4], target = 7 +Output: 13 +(There are 13 subsets with sum ≤ 7) +--------------------------------------------------------- +*/ + +#include +using namespace std; + +// ----------------------------------------------------- +// Helper function to generate all subset sums +// ----------------------------------------------------- +void generateSubsetSums(const vector& nums, vector& subsetSums) { + int n = nums.size(); + int totalSubsets = 1 << n; // 2^n possible subsets + for (int mask = 0; mask < totalSubsets; ++mask) { + long long sum = 0; + for (int i = 0; i < n; ++i) { + if (mask & (1 << i)) + sum += nums[i]; + } + subsetSums.push_back(sum); + } +} + +// ----------------------------------------------------- +// Main function to count subsets with sum ≤ target +// ----------------------------------------------------- +long long countSubsets(vector& arr, int target) { + int n = arr.size(); + int mid = n / 2; + + // Divide array into two halves + vector leftArr(arr.begin(), arr.begin() + mid); + vector rightArr(arr.begin() + mid, arr.end()); + + // Generate subset sums for both halves + vector leftSums, rightSums; + generateSubsetSums(leftArr, leftSums); + generateSubsetSums(rightArr, rightSums); + + // Sort right half to enable binary search + sort(rightSums.begin(), rightSums.end()); + + long long count = 0; + + // For each sum in left half, find how many rightSums can be added + // without exceeding the target + for (auto& sumLeft : leftSums) { + long long remaining = target - sumLeft; + // upper_bound gives iterator to first element > remaining + count += upper_bound(rightSums.begin(), rightSums.end(), remaining) - rightSums.begin(); + } + + return count; +} + +// ----------------------------------------------------- +// Driver Code (Test) +// ----------------------------------------------------- +int main() { + vector arr = {3, 1, 2, 5, 4}; + int target = 7; + + cout << "Array: "; + for (int num : arr) cout << num << " "; + cout << "\nTarget: " << target << "\n"; + + long long result = countSubsets(arr, target); + cout << "Number of subsets with sum less than equal to target = " << result << endl; + + return 0; +} + +/* +--------------------------------------------------------- +🧠 Explanation: + +For arr = [3, 1, 2, 5, 4], N = 5 → split into: +Left = [3, 1], Right = [2, 5, 4] + +Left subset sums = [0, 3, 1, 4] +Right subset sums = [0, 2, 5, 4, 7, 6, 9, 11] + +For each left sum, count how many right sums fit: +Example: left=3 → right sums ≤ 4 → {0,2,4} (3 ways) + +Final count = 13 subsets with total sum ≤ 7. +--------------------------------------------------------- +*/ diff --git a/Algorithms/Problem_Solving/README.md b/Algorithms/Problem_Solving/README.md new file mode 100644 index 0000000000..ba44dada11 --- /dev/null +++ b/Algorithms/Problem_Solving/README.md @@ -0,0 +1,26 @@ +# ⚙️ Meet in the Middle — Algorithmic Technique + +## 📘 Overview + +**Meet in the Middle (MITM)** is an optimization technique used to reduce the time complexity of problems that would otherwise require exploring all possible subsets (`O(2^N)`). + +The key idea: + +> Instead of generating all possible combinations directly, we **split the problem into two halves**, solve each half independently, and then **combine** the results efficiently. + +--- + +## 🧠 Why It Works + +For problems like subset sum, product optimization, or combinatorial selection: + +- Direct brute-force → `O(2^N)` → infeasible when `N > 30` +- Meet in the Middle → `O(2^(N/2) * log(2^(N/2)))` → manageable for `N ≤ 40` + +It leverages: + +- **Divide and Conquer** +- **Binary Search / Hashing** +- **Combination of precomputed subsets** + +---