Halfling Woolly Proudhoof is an eminent sheep herder. He wants to build a pen (enclosure) for his new flock of sheep. The pen will be rectangular and built from exactly four pieces of fence (so, the pieces of fence forming the opposite sides of the pen must be of equal length). Woolly can choose these pieces out of N pieces of fence that are stored in his barn. To hold the entire flock, the area of the pen must be greater than or equal to a given threshold X.
Woolly is interested in the number of different ways in which he can build a pen. Pens are considered different if the sets of lengths of their sides are different. For example, a pen of size 1×4 is different from a pen of size 2×2 (although both have an area of 4), but pens of sizes 1×2 and 2×1 are considered the same.
Write a function:
int solution(vector<int> &A, int X);
that, given an array A of N integers (containing the lengths of the available pieces of fence) and an integer X, returns the number of different ways of building a rectangular pen satisfying the above conditions. The function should return −1 if the result exceeds 1,000,000,000.
For example, given X = 5 and the following array A:
A[0] = 1 A[1] = 2 A[2] = 5 A[3] = 1 A[4] = 1 A[5] = 2 A[6] = 3 A[7] = 5 A[8] = 1the function should return 2. The figure above shows available pieces of fence (on the left) and possible to build pens (on the right). The pens are of sizes 1x5 and 2x5. Pens of sizes 1×1 and 1×2 can be built, but are too small in area. It is not possible to build pens of sizes 2×3 or 3×5, as there is only one piece of fence of length 3.
Write an efficient algorithm for the following assumptions:
- N is an integer within the range [0..100,000];
- X is an integer within the range [1..1,000,000,000];
- each element of array A is an integer within the range [1..1,000,000,000].
#include <unordered_map>
#include <cmath>
#include <algorithm>
using namespace std;
// helper function to avoid integer overflow when multiplying large ints
bool is_greater_or_equal(int v, int w, int X) {
long long prod = v;
prod *= w;
return prod >= X;
}
// binary search to find the smaller suitable fence
int binary_search(int back, vector<int> &fences, int begin, int end, int X) {
if (begin < end) {
int mid = (begin + end) / 2;
if (is_greater_or_equal(back, fences[mid], X)) {
return binary_search(back, fences, begin, mid, X);
} else {
return binary_search(back, fences, mid + 1, end, X);
}
} else {
return begin;
}
}
int solution(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
//make sense to check if a suitable solution even exists
if (is_greater_or_equal(max_fence, max_fence, X)) {
double x = X;
// what is the min possible fence size given X and the max lenghty fence?
int min_fances = static_cast<int>(std::ceil(x / max_fence));
// filtering only viable fences
vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
//sort fences (sure captain obvious)
sort(fences.begin(), fences.end());
const int SIZE = fences.size();
for(int i = 0; i < SIZE; ++i) {
int fence = fences[i];
int smaller_suitable_fence_index = binary_search(fence, fences, i, SIZE - 1, X);
result += SIZE - smaller_suitable_fence_index;
// wait! wait! WTF is this sum above?
// Okay, let me explain.
// suppose we have 4 fences: 12, 13, 16 and 19 to build a greater or equal to 200 pen.
// The table of combinations is:
// 12 13 16 19
// 12 144 156 192 228
// 13 156 169 208 247
// 15 180 195 240 285
// 16 192 208 256 304
// 19 228 247 304 361
// for first row where fence is 12 the smaller suitable fence is 19 because of 12x19 = 228.
// for fence = 12, we cannot use a smaller fence like 16 because 12x16 is 192 which
// is less than 200. So, we choose 19 as pair to 12 and sum 1 to result
// the next row, fence = 13, the smaller suitable fence is 16 (13x16 is 208) and make result += 2
// note that we start searching from the current fence index to avoid duplicates such (X, Y) and (Y, X)
// combinations such as (X, X) are only allowed if there are at least 4 fences available
if (i == smaller_suitable_fence_index and not_used_fences_map[fences[i]] < 4)
result--;
if (result >= 1'000'000'000) return -1;
}
}
return result;
}
// solution achieves 100% correctness but only O(N**2) time complexity
int solution_brute_force(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
double x = X;
int min_fances = static_cast<int>(std::ceil(x / max_fence));
std::vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
for (long long v : fences) {
for (long long w : fences) {
if (v == w && (not_used_fences_map[v] < 4 || not_used_fences_map[w] < 4)) {
continue;
}
if (v < w) {
continue;
}
if (v * w >= X) {
result++;
if (result >= 1'000'000'000) return -1;
}
}
}
return result;
}
#include <unordered_map>
#include <cmath>
#include <algorithm>
using namespace std;
// helper function to avoid integer overflow when multiplying large ints
bool is_greater_or_equal(int v, int w, int X) {
long long prod = v;
prod *= w;
return prod >= X;
}
// binary search to find the smaller suitable fence
int binary_search(int back, vector<int> &fences, int begin, int end, int X) {
if (begin < end) {
int mid = (begin + end) / 2;
if (is_greater_or_equal(back, fences[mid], X)) {
return binary_search(back, fences, begin, mid, X);
} else {
return binary_search(back, fences, mid + 1, end, X);
}
} else {
return begin;
}
}
int solution(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
//make sense to check if a suitable solution even exists
if (is_greater_or_equal(max_fence, max_fence, X)) {
double x = X;
// what is the min possible fence size given X and the max lenghty fence?
int min_fances = static_cast<int>(std::ceil(x / max_fence));
// filtering only viable fences
vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
//sort fences (sure captain obvious)
sort(fences.begin(), fences.end());
const int SIZE = fences.size();
for(int i = 0; i < SIZE; ++i) {
int fence = fences[i];
int smaller_suitable_fence_index = binary_search(fence, fences, i, SIZE - 1, X);
result += SIZE - smaller_suitable_fence_index;
// wait! wait! WTF is this sum above?
// Okay, let me explain.
// suppose we have 4 fences: 12, 13, 16 and 19 to build a greater or equal to 200 pen.
// The table of combinations is:
// 12 13 16 19
// 12 144 156 192 228
// 13 156 169 208 247
// 15 180 195 240 285
// 16 192 208 256 304
// 19 228 247 304 361
// for first row where fence is 12 the smaller suitable fence is 19 because of 12x19 = 228.
// for fence = 12, we cannot use a smaller fence like 16 because 12x16 is 192 which
// is less than 200. So, we choose 19 as pair to 12 and sum 1 to result
// the next row, fence = 13, the smaller suitable fence is 16 (13x16 is 208) and make result += 2
// note that we start searching from the current fence index to avoid duplicates such (X, Y) and (Y, X)
// combinations such as (X, X) are only allowed if there are at least 4 fences available
if (i == smaller_suitable_fence_index and not_used_fences_map[fences[i]] < 4)
result--;
if (result >= 1'000'000'000) return -1;
}
}
return result;
}
// solution achieves 100% correctness but only O(N**2) time complexity
int solution_brute_force(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
double x = X;
int min_fances = static_cast<int>(std::ceil(x / max_fence));
std::vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
for (long long v : fences) {
for (long long w : fences) {
if (v == w && (not_used_fences_map[v] < 4 || not_used_fences_map[w] < 4)) {
continue;
}
if (v < w) {
continue;
}
if (v * w >= X) {
result++;
if (result >= 1'000'000'000) return -1;
}
}
}
return result;
}
#include <unordered_map>
#include <cmath>
#include <algorithm>
using namespace std;
// helper function to avoid integer overflow when multiplying large ints
bool is_greater_or_equal(int v, int w, int X) {
long long prod = v;
prod *= w;
return prod >= X;
}
// binary search to find the smaller suitable fence
int binary_search(int back, vector<int> &fences, int begin, int end, int X) {
if (begin < end) {
int mid = (begin + end) / 2;
if (is_greater_or_equal(back, fences[mid], X)) {
return binary_search(back, fences, begin, mid, X);
} else {
return binary_search(back, fences, mid + 1, end, X);
}
} else {
return begin;
}
}
int solution(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
//make sense to check if a suitable solution even exists
if (is_greater_or_equal(max_fence, max_fence, X)) {
double x = X;
// what is the min possible fence size given X and the max lenghty fence?
int min_fances = static_cast<int>(std::ceil(x / max_fence));
// filtering only viable fences
vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
//sort fences (sure captain obvious)
sort(fences.begin(), fences.end());
const int SIZE = fences.size();
for(int i = 0; i < SIZE; ++i) {
int fence = fences[i];
int smaller_suitable_fence_index = binary_search(fence, fences, i, SIZE - 1, X);
result += SIZE - smaller_suitable_fence_index;
// wait! wait! WTF is this sum above?
// Okay, let me explain.
// suppose we have 4 fences: 12, 13, 16 and 19 to build a greater or equal to 200 pen.
// The table of combinations is:
// 12 13 16 19
// 12 144 156 192 228
// 13 156 169 208 247
// 15 180 195 240 285
// 16 192 208 256 304
// 19 228 247 304 361
// for first row where fence is 12 the smaller suitable fence is 19 because of 12x19 = 228.
// for fence = 12, we cannot use a smaller fence like 16 because 12x16 is 192 which
// is less than 200. So, we choose 19 as pair to 12 and sum 1 to result
// the next row, fence = 13, the smaller suitable fence is 16 (13x16 is 208) and make result += 2
// note that we start searching from the current fence index to avoid duplicates such (X, Y) and (Y, X)
// combinations such as (X, X) are only allowed if there are at least 4 fences available
if (i == smaller_suitable_fence_index and not_used_fences_map[fences[i]] < 4)
result--;
if (result >= 1'000'000'000) return -1;
}
}
return result;
}
// solution achieves 100% correctness but only O(N**2) time complexity
int solution_brute_force(vector<int> &A, int X) {
const int N = A.size();
if (N < 4) {
return 0;
}
unordered_map<int, int> not_used_fences_map;
for (int v : A) {
if (not_used_fences_map.find(v) == not_used_fences_map.end())
not_used_fences_map[v] = 1;
else {
not_used_fences_map[v]++;
}
}
std::vector<int> not_used_fences;
int max_fence = 0;
for (const auto & elem : not_used_fences_map) {
if (elem.second > 1) {
max_fence = std::max(max_fence, elem.first);
not_used_fences.push_back(elem.first);
}
}
int result = 0;
double x = X;
int min_fances = static_cast<int>(std::ceil(x / max_fence));
std::vector<int> fences;
for (int v : not_used_fences) {
if (v >= min_fances) {
fences.push_back(v);
}
}
for (long long v : fences) {
for (long long w : fences) {
if (v == w && (not_used_fences_map[v] < 4 || not_used_fences_map[w] < 4)) {
continue;
}
if (v < w) {
continue;
}
if (v * w >= X) {
result++;
if (result >= 1'000'000'000) return -1;
}
}
}
return result;
}
The solution obtained perfect score.