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
Original file line number Diff line number Diff line change
@@ -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 <bits/stdc++.h>
using namespace std;

// -----------------------------------------------------
// Helper: Generate all subsets with (cost, value) pairs
// -----------------------------------------------------
void generateSubsets(const vector<pair<int, int>>& items, vector<pair<long long, long long>>& 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<pair<int, int>>& items, int budget) {
int n = items.size();
int mid = n / 2;

// Split into two halves
vector<pair<int, int>> leftItems(items.begin(), items.begin() + mid);
vector<pair<int, int>> rightItems(items.begin() + mid, items.end());

// Generate all subset pairs (cost, value)
vector<pair<long long, long long>> 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<pair<long long, long long>> 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<pair<int, int>> 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.
---------------------------------------------------------
*/
122 changes: 122 additions & 0 deletions Algorithms/Problem_Solving/Meet_in_the_Middle/Max_Product_Subset.cpp
Original file line number Diff line number Diff line change
@@ -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 <bits/stdc++.h>
using namespace std;

// -----------------------------------------------------
// Helper function: generate all subset products
// -----------------------------------------------------
void generateSubsetProducts(const vector<int>& nums, vector<long long>& 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<int>& arr) {
int n = arr.size();
int mid = n / 2;

// Split into two halves
vector<int> leftArr(arr.begin(), arr.begin() + mid);
vector<int> rightArr(arr.begin() + mid, arr.end());

// Generate subset products
vector<long long> 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<int> 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
---------------------------------------------------------
*/
115 changes: 115 additions & 0 deletions Algorithms/Problem_Solving/Meet_in_the_Middle/Subset_Sum_Variant.cpp
Original file line number Diff line number Diff line change
@@ -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 <bits/stdc++.h>
using namespace std;

// -----------------------------------------------------
// Helper function to generate all subset sums
// -----------------------------------------------------
void generateSubsetSums(const vector<int>& nums, vector<long long>& 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<int>& arr, int target) {
int n = arr.size();
int mid = n / 2;

// Divide array into two halves
vector<int> leftArr(arr.begin(), arr.begin() + mid);
vector<int> rightArr(arr.begin() + mid, arr.end());

// Generate subset sums for both halves
vector<long long> 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<int> 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.
---------------------------------------------------------
*/
Loading