/*
Problem: Maximum Size of Infinity Stone
Company Tag: Media.net OA
Problem Statement:
We are given a 2D array A of size N x k.
There are N shops.
Each shop sells k types of nanocrystals.
A[i][j] means the size of type j crystal sold by shop i.
We can choose at most 2 shops.
For every type j:
best[j] = maximum crystal size among chosen shops
To make an infinity stone of size X:
cost for type j = ceil(X / best[j])
Total cost must be <= S.
Return maximum possible X modulo 1e9 + 7.
Constraints:
1 <= N <= 100000
1 <= k <= 7
1 <= A[i][j] <= 100000
1 <= S <= 1000000000
Brute Force:
Try every pair of shops and every possible value of X.
Why Brute Force Fails:
N can be 100000.
Trying every pair of shops is O(N^2), which is too slow.
Optimized Idea:
I binary search the answer X.
For fixed X:
I check whether X can be made within budget S using at most 2 shops.
Since k <= 7, I use bitmasking.
If k = 3:
mask 101 means crystal types 0 and 2 are handled by one shop.
Explanation Block:
For every shop, I calculate how much it costs to produce size X
for each crystal type.
Then for every subset mask:
best[mask] = minimum cost to cover that subset using one shop.
Since I can choose at most 2 shops:
I split all crystal types into two groups.
If:
best[group1] + best[group2] <= S
then X is possible.
Dry Run:
A =
[
[9, 4, 1],
[2, 5, 10],
[6, 8, 3]
]
S = 10
Choose shop 2 and shop 3:
best = [6, 8, 10]
For X = 24:
ceil(24 / 6) = 4
ceil(24 / 8) = 3
ceil(24 / 10) = 3
Total cost = 10
Answer = 24
Time Complexity:
O(log(maxAnswer) * N * 2^k)
Space Complexity:
O(2^k)
*/
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 1000000007LL;
bool canMakeStone(vector<vector<int>>& A, ll S, ll X) {
int n = A.size();
int k = A[0].size();
int totalMasks = 1 << k;
// Any cost greater than S is useless because budget is only S.
ll INF = S + 1;
// best[mask] = minimum cost to cover this subset using one shop.
vector<ll> best(totalMasks, INF);
best[0] = 0;
for (int i = 0; i < n; i++) {
vector<ll> cost(k);
// Calculate cost of each crystal type from current shop.
for (int j = 0; j < k; j++) {
cost[j] = (X + A[i][j] - 1) / A[i][j];
if (cost[j] > S) {
cost[j] = INF;
}
}
vector<ll> subsetCost(totalMasks, 0);
// Calculate cost for every subset of crystal types for this shop.
for (int mask = 1; mask < totalMasks; mask++) {
int bit = __builtin_ctz(mask);
int previousMask = mask ^ (1 << bit);
subsetCost[mask] = subsetCost[previousMask] + cost[bit];
if (subsetCost[mask] > S) {
subsetCost[mask] = INF;
}
}
// Update minimum one-shop cost for every subset.
for (int mask = 0; mask < totalMasks; mask++) {
best[mask] = min(best[mask], subsetCost[mask]);
}
}
int fullMask = totalMasks - 1;
// Split all crystal types into two groups.
// One group is handled by one shop, the other by another shop.
for (int mask = 0; mask < totalMasks; mask++) {
int other = fullMask ^ mask;
if (best[mask] + best[other] <= S) {
return true;
}
}
return false;
}
int maximumInfinityStoneSize(vector<vector<int>>& A, ll S) {
int maxCrystal = 0;
for (auto& row : A) {
for (int x : row) {
maxCrystal = max(maxCrystal, x);
}
}
ll low = 0;
ll high = 1LL * maxCrystal * S;
ll ans = 0;
// Binary search maximum possible stone size.
while (low <= high) {
ll mid = low + (high - low) / 2;
if (canMakeStone(A, S, mid)) {
ans = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
return ans % MOD;
}
int main() {
vector<vector<int>> A = {
{9, 4, 1},
{2, 5, 10},
{6, 8, 3}
};
long long S = 10;
cout << maximumInfinityStoneSize(A, S) << endl;
return 0;
}