I'm trying to write a program whose input is an array of integers, and its size. This code has to delete each element which is smaller than the element to the left. We want to find number of times that we can process the array this way, until we can no longer delete any more elements.
The contents of the array after we return are unimportant - only the return value is of interest.
For example: given the array [10, 9, 7, 8, 6, 5, 3, 4, 2, 1], the function should return 2, because:
[10,9,7,8,6,5,3,4,2,1] → [10,8,4] → [10]
For example: given the array [1,2,3,4], the function should return 0, because
No element is larger than the element to its right
I want each element to remove the right element if it is more than its right element. We get a smaller array. Then we repeat this operation again. Until we get to an array in which no element can delete another element. I want to calculate the number of steps performed.
int Mafia(int n, vector <int> input_array)
{
int ptr = n;
int last_ptr = n;
int night_Count = 0;
do
{
last_ptr = ptr;
ptr = 1;
for (int i = 1; i < last_ptr; i++)
{
if (input_array[i] >= input_array[i - 1])
{
input_array[ptr++] = input_array[i];
}
}
night_Count++;
} while (last_ptr > ptr);
return night_Count - 1;
}
My code works but I want it to be faster.
Do you have any idea to make this code faster, or another way that is faster than this?
Here is a O(NlogN) solution.
The idea is to iterate over the array and keep tracking candidateKillers which could kill unvisited numbers. Then we find the killer for the current number by using binary search and update the maximum iterations if needed.
Since we iterate over the array which has N numbers and apply log(N) binary search for each number, the overall time complexity is O(NlogN).
Alogrithm
If the current number is greater or equal than the number before it, it could be a killer for numbers after it.
For each killer, we keep tracking its index idx, the number of it num and the iterations needed to reach that killer iters.
The numbers in the candidateKillers by its nature are non-increasing (see next point). Therefore we can apply binary search to find the killer of the current number, which is the one that is a) the closest to the current number b) greater than the current number. This is implemented in searchKiller.
If the current number will be killed by a number in candidateKillers with killerPos, then all candidate killers after killerPos are outdated, because those outdated killers will be killed before the numbers after the current number reach them. If the current number is greater than all candidateKillers, then all the candidateKillers can be discarded.
When we find the killer of the current number, we increase the iters of the killer by one. Because from now on, one more iteration is needed to reach that killer where the current number need to be killed first.
class Solution {
public:
int countIterations(vector<int>& array) {
if (array.size() <= 1) {
return 0;
}
int ans = 0;
vector<Killer> candidateKillers = {Killer(0, array[0], 1)};
for (auto i = 1; i < array.size(); i++) {
int curNum = array[i];
int killerPos = searchKiller(candidateKillers, curNum);
if (killerPos == -1) {
// current one is the largest so far and all candidateKillers before are outdated
candidateKillers = {Killer(i, curNum, 1)};
continue;
}
// get rid of outdated killers
int popCount = candidateKillers.size() - 1 - killerPos;
for (auto j = 0; j < popCount; j++) {
candidateKillers.pop_back();
}
Killer killer = candidateKillers[killerPos];
ans = max(killer.iters, ans);
if (curNum < array[i-1]) {
// since the killer of the current one may not even be in the list e.g., if current is 4 in [6,5,4]
if (killer.idx == i - 1) {
candidateKillers[killerPos].iters += 1;
}
} else {
candidateKillers[killerPos].iters += 1;
candidateKillers.push_back(Killer(i, curNum, 1));
}
}
return ans;
}
private:
struct Killer {
Killer(int idx, int num, int iters)
: idx(idx), num(num), iters(iters) {};
int idx;
int num;
int iters;
};
int searchKiller(vector<Killer>& candidateKillers, int n) {
int lo = 0;
int hi = candidateKillers.size() - 1;
if (candidateKillers[0].num < n) {
return -1;
}
int ans = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (candidateKillers[mid].num > n) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
};
int main() {
vector<int> array1 = {10, 9, 7, 8, 6, 5, 3, 4, 2, 1};
vector<int> array2 = {1, 2, 3, 4};
vector<int> array3 = {4, 2, 1, 2, 3, 3};
cout << Solution().countIterations(array1) << endl; // 2
cout << Solution().countIterations(array2) << endl; // 0
cout << Solution().countIterations(array3) << endl; // 4
}
You can iterate in reverse, keeping two iterators or indices and moving elements in place. You don't need to allocate a new vector or even resize existing vector. Also a minor, but can replace recursion with loop or write the code the way compiler likely to do it.
This approach is still O(n^2) worst case but it would be faster in run time.
Related
How to divide elements in an array into a minimum number of arrays such that the difference between the values of elements of each of the formed arrays does not differ by more than 1?
Let's say that we have an array: [4, 6, 8, 9, 10, 11, 14, 16, 17].
The array elements are sorted.
I want to divide the elements of the array into a minimum number of array(s) such that each of the elements in the resulting arrays do not differ by more than 1.
In this case, the groupings would be: [4], [6], [8, 9, 10, 11], [14], [16, 17]. So there would be a total of 5 groups.
How can I write a program for the same? Or you can suggest algorithms as well.
I tried the naive approach:
Obtain the difference between consecutive elements of the array and if the difference is less than (or equal to) 1, I add those elements to a new vector. However this method is very unoptimized and straight up fails to show any results for a large number of inputs.
Actual code implementation:
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
int main() {
int num = 0, buff = 0, min_groups = 1; // min_groups should start from 1 to take into account the grouping of the starting array element(s)
cout << "Enter the number of elements in the array: " << endl;
cin >> num;
vector<int> ungrouped;
cout << "Please enter the elements of the array: " << endl;
for (int i = 0; i < num; i++)
{
cin >> buff;
ungrouped.push_back(buff);
}
for (int i = 1; i < ungrouped.size(); i++)
{
if ((ungrouped[i] - ungrouped[i - 1]) > 1)
{
min_groups++;
}
}
cout << "The elements of entered vector can be split into " << min_groups << " groups." << endl;
return 0;
}
Inspired by Faruk's answer, if the values are constrained to be distinct integers, there is a possibly sublinear method.
Indeed, if the difference between two values equals the difference between their indexes, they are guaranteed to belong to the same group and there is no need to look at the intermediate values.
You have to organize a recursive traversal of the array, in preorder. Before subdividing a subarray, you compare the difference of indexes of the first and last element to the difference of values, and only subdivide in case of a mismatch. As you work in preorder, this will allow you to emit pieces of the groups in consecutive order, as well as detect to the gaps. Some care has to be taken to merge the pieces of the groups.
The worst case will remain linear, because the recursive traversal can degenerate to a linear traversal (but not worse than that). The best case can be better. In particular, if the array holds a single group, it will be found in time O(1). If I am right, for every group of length between 2^n and 2^(n+1), you will spare at least 2^(n-1) tests. (In fact, it should be possible to estimate an output-sensitive complexity, equal to the array length minus a fraction of the lengths of all groups, or similar.)
Alternatively, you can work in a non-recursive way, by means of exponential search: from the beginning of a group, you start with a unit step and double the step every time, until you detect a gap (difference in values too large); then you restart with a unit step. Here again, for large groups you will skip a significant number of elements. Anyway, the best case can only be O(Log(N)).
I would suggest encoding subsets into an offset array defined as follows:
Elements for set #i are defined for indices j such that offset[i] <= j < offset[i+1]
The number of subsets is offset.size() - 1
This only requires one memory allocation.
Here is a complete implementation:
#include <cassert>
#include <iostream>
#include <vector>
std::vector<std::size_t> split(const std::vector<int>& to_split, const int max_dist = 1)
{
const std::size_t to_split_size = to_split.size();
std::vector<std::size_t> offset(to_split_size + 1);
offset[0] = 0;
size_t offset_idx = 1;
for (std::size_t i = 1; i < to_split_size; i++)
{
const int dist = to_split[i] - to_split[i - 1];
assert(dist >= 0); // we assumed sorted input
if (dist > max_dist)
{
offset[offset_idx] = i;
++offset_idx;
}
}
offset[offset_idx] = to_split_size;
offset.resize(offset_idx + 1);
return offset;
}
void print_partition(const std::vector<int>& to_split, const std::vector<std::size_t>& offset)
{
const std::size_t offset_size = offset.size();
std::cout << "\nwe found " << offset_size-1 << " sets";
for (std::size_t i = 0; i + 1 < offset_size; i++)
{
std::cout << "\n";
for (std::size_t j = offset[i]; j < offset[i + 1]; j++)
{
std::cout << to_split[j] << " ";
}
}
}
int main()
{
std::vector<int> to_split{4, 6, 8, 9, 10, 11, 14, 16, 17};
std::vector<std::size_t> offset = split(to_split);
print_partition(to_split, offset);
}
which prints:
we found 5 sets
4
6
8 9 10 11
14
16 17
Iterate through the array. Whenever the difference between 2 consecutive element is greater than 1, add 1 to your answer variable.
`
int getPartitionNumber(int arr[]) {
//let n be the size of the array;
int result = 1;
for(int i=1; i<n; i++) {
if(arr[i]-arr[i-1] > 1) result++;
}
return result;
}
`
And because it is always nice to see more ideas and select the one that suites you best, here the straight forward 6 line solution. Yes, it is also O(n). But I am not sure, if the overhead for other methods makes it faster.
Please see:
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <iterator>
using Data = std::vector<int>;
using Partition = std::vector<Data>;
Data testData{ 4, 6, 8, 9, 10, 11, 14, 16, 17 };
int main(void)
{
// This is the resulting vector of vectors with the partitions
std::vector<std::vector<int>> partition{};
// Iterating over source values
for (Data::iterator i = testData.begin(); i != testData.end(); ++i) {
// Check,if we need to add a new partition
// Either, at the beginning or if diff > 1
// No underflow, becuase of boolean shortcut evaluation
if ((i == testData.begin()) || ((*i) - (*(i-1)) > 1)) {
// Create a new partition
partition.emplace_back(Data());
}
// And, store the value in the current partition
partition.back().push_back(*i);
}
// Debug output: Copy all data to std::cout
std::for_each(partition.begin(), partition.end(), [](const Data& d) {std::copy(d.begin(), d.end(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\n'; });
return 0;
}
Maybe this could be a solution . . .
How do you say your approach is not optimized? If your is correct, then according to your approach, it takes O(n) time complexity.
But you can use binary-search here which can optimize in average case. But in worst case this binary search can take more than O(n) time complexity.
Here's a tips,
As the array sorted so you will pick such a position whose difference is at most 1.
Binary search can do this in simple way.
int arr[] = [4, 6, 8, 9, 10, 11, 14, 16, 17];
int st = 0, ed = n-1; // n = size of the array.
int partitions = 0;
while(st <= ed) {
int low = st, high = n-1;
int pos = low;
while(low <= high) {
int mid = (low + high)/2;
if((arr[mid] - arr[st]) <= 1) {
pos = mid;
low = mid + 1;
} else {
high = mid - 1;
}
}
partitions++;
st = pos + 1;
}
cout<< partitions <<endl;
In average case, it is better than O(n). But in worst case (where the answer would be equal to n) it takes O(nlog(n)) time.
I have a homework problem in C++ that I could (and did) solve, but not fast enough.
So the problem goes like this: On a platform, there are n bars of equal width and height. It starts raining. Find out the quantity of water that fits in between the bars (Very bad enunciation , I know, it's better to look at the example). Examples:
n = 6
bar lengths = {3, 0, 0, 2, 0, 4}
Answer would be = 10
The cubes of water would "fill out" the empty space between the bars and I need the find the number of cubes:
Explanation:
Another example:
n = 12
bar lengths = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}
Answer = 6
What I tried:
For each spot in the array, I found the maximum height bar to the left of it and to the right of it and then I "filled" this spot with the minimum between the maximum to the left and the maximum to the right minus the height of the bar at the present spot:
#include <iostream>
using namespace std;
int main() {
int n, a[100001], i, j, volume=0, max_left, max_right;
cin >> n;
// Input the array
for (i=0; i<n; i++) {
cin >> a[i];
}
// For each element (except the first and last)
for (i=1; i<(n-1); i++) {
max_left = max_right = a[i];
// Find the maximum to the left of it and to the right of it
for (j=0; j<i; j++) {
if (a[j] > max_left) {
max_left = a[j];
}
}
for (j=(i+1); j<n; j++) {
if (a[j] > max_right) {
max_right = a[j];
}
}
// The quantity of water that fits on this spot is equal to
// the minimum between the maxes, minus the height of the
// bar in this spot
volume += (min(max_left, max_right) - a[i]);
}
cout << volume;
return 0;
}
The solution is good, I get the corrent results. But the speed is a problem. I believe the complexity of this solution is O(n^2), if I'm not mistaken. Now the problem has to be solved in O(n). The problem is: How can I find the maxes in both directions for each element in O(n)? Any help will be appreciated. Thanks!
Find the highest bar in the complete list. This gives to sub-ranges Before and After (both excluding the highest bar).
Iterate over both sub-ranges (front to back for Before, back to front for After): Remember the highest bar you've found on the way, starting with 0. Add the difference of the current height to the result.
Add both results.
This works because once you've found the overall maximum height, all other heights for Front and Back are at least lower or equal than the maximum. This way you can skip searching in both directions and simply use the highest bar you've met so far.
Both steps are O(n). Here is an example:
#include <algorithm>
#include <cassert>
#include <iostream>
#include <numeric>
#include <vector>
template <typename First, typename Last>
int calcRange(First begin, Last end) {
int max{0};
int result{0};
for (auto it = begin; it != end; ++it) {
const auto current = *it;
result += std::max(0, max - current);
max = std::max(max, current);
}
return result;
}
int calc(const std::vector<int>& bars) {
if (bars.size() <= 1) return 0;
// find max = O(n)
const auto maxIt = std::max_element(bars.cbegin(), bars.cend());
assert(maxIt != bars.cend());
// calculate left and right = O(n)
const auto l = calcRange(bars.cbegin(), maxIt);
const auto r = calcRange(bars.crbegin(), std::make_reverse_iterator(maxIt));
return l + r;
}
int main() {
std::cout << calc({3, 0, 0, 2, 0, 4}) << std::endl;
std::cout << calc({0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}) << std::endl;
}
I have to say, that I really liked this question.
This might give you an idea on how to solve this question. Basically, you are looking to leftmost and rightmost bar heights. Then you will raise the waterlevel to the minimum of both and compute the amount of water needed for this. Afterwards you can shrink the bars array and repeat the process.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> bars{ 3, 0, 0, 2, 0, 4 };
int waterCounter = 0;
int waterLevel = 0;
auto leftIter = bars.begin();
auto rightIter = bars.end() - 1;
while (true)
{
if (leftIter == rightIter) break;
auto newWaterLevel = std::min(*leftIter, *rightIter);
if (newWaterLevel > waterLevel)
{
auto newRight = std::next(rightIter);
auto size=std::distance(leftIter, newRight);
auto waterInGaps = 0;
for (auto iter=leftIter; iter!=newRight; iter++ )
{
waterInGaps += *iter > newWaterLevel ? 0 : newWaterLevel-*iter;
*iter = *iter>newWaterLevel?*iter:newWaterLevel;
}
waterCounter += waterInGaps;
}
while (leftIter!=rightIter)
{
if (*leftIter > newWaterLevel) break;
std::advance(leftIter, 1);
}
while (rightIter!=leftIter)
{
if (*rightIter > newWaterLevel) break;
std::advance(rightIter, -1);
}
waterLevel = newWaterLevel;
}
std::cout << waterCounter << std::endl;
return EXIT_SUCCESS;
}
Thanks a lot everyone, your ideas helped! As I am not that advanced, I couldn't (and didn't really know how to) use vectors, auto (this one seems like magic to me), templates and other things. If anyone is still interested, this is the code I used and I got 100 points on the site:
#include <iostream>
using namespace std;
int main()
{
int n, a[100001], left_max[100001], right_max[100001];
int i, max_to_right, max_to_left, volume=0;
cin >> n;
for (i=0; i<n; i++) {
// Input the array
cin >> a[i];
// Directly find the "maximum to the left" of each element
if (i == 0) {
left_max[i] = max_to_left = a[i];
}
else {
if (a[i] > max_to_left) {
max_to_left = a[i];
}
left_max[i] = max_to_left;
}
}
// Not the only thing left is to find the "maximum to the right" of each element
for (i=(n-1); i>=0; i--) {
if (i == (n-1)) {
right_max[i] = max_to_right = a[i];
}
else {
if (a[i] > max_to_right) {
max_to_right = a[i];
}
right_max[i] = max_to_right;
}
// No need to have another loop afterwards, add to volume as we go
if (i>0 && i<(n-1)) {
volume += (min(left_max[i], right_max[i]) - a[i]);
}
}
cout << volume;
return 0;
}
I basically did the same thing, but faster. I found the maximum to the right and to the left of each element, but I found the maximum to the left of each element while reading the input and then with another loop I found the maximum of each element, but to the right. The website had a very similar solution, just a bit shorter.
I would start off by asking off a few questions which would get me close to a solution. Think of it this way, if you poured water into the structure above, the empty spots would only ever accumulate to the right or the left of the 'Highest bar'. Once you have that insight, now from left to the highest bar do the following, currbar_from_left = -1 and the current value set to start of the array.
1- if the currbar_from_left > current then currbar_from_left = current
else ans = ans + (currbar - currbar_from_left) - add the difference to the answer since we know definately that this would accmulate compared to our anchor point(the highest bar)
2- now do another traversal, this time from right of the array to the highest point. currbar_from_right = -1 and current set to the last value in the array
if the currbar_from_right > current then currbar_from_right = current
else ans = ans + (currbar - curbar_from_right)
3- The ans is now the total empty cubes.
You could combine steps 1 and 2 into a single loop with the conditions as they are but the above algorithm is more clear in terms of understanding.
The following code demostrates this:
int n, a[100001], i, j, volume = 0, max_left, max_right;
cin >> n;
// Input the array
for (i = 0; i<n; i++) {
cin >> a[i];
}
// Maximum height in the array
int maximum_bar = -1, maximum_index = -1;
for (i = 0; i < n; i++) {
if (a[i] > maximum_bar)
{
maximum_bar = a[i];
// where did i see it?
maximum_index = i;
}
}
//Left search, to the maximum_bar
int left = -1;
for (i = 0; i < maximum_index; i++) {
if (a[i] > left) left = a[i];
else volume = volume + (left - a[i]);
}
//Right search, to the maximum_bar
int right = -1;
for (i = n - 1; i >= maximum_index; i--) {
if (a[i] > right) right = a[i];
else volume = volume + (right - a[i]);
}
cout << volume;
return 0;
Let's try a different approach. We can do this in a single pass from left to right, except for a special case at the end of the array.
Consider that we're iterating from left to right, keeping track of what's the current water level. Each of these scenarios can happen:
We reached a column that is higher than the water level;
We reached a column that is lower than the water level;
We reached a column that has the same height as the water level;
The first one defines a new water level. For the two others, notice that to count the blocks, all we need to do is add water_level - height to the current total.
This will only cause a problem once we reach the end. Consider this input, for instance:
{2, 0, 0, 3, 0, 0}
Notice that the water level on the two last items should be 0, but we have just set it to 3! To fix this, we simply discard the result of the last iteration (from 3 to the end) and do a reverse iteration from the end to that point.
If this all sounds a bit tricky, you will see that the implementation is actually quite simple. Following is what I came up with: It uses some recursion to simplify things, and works on any pair of iterators:
#include <iterator> // std::reverse_iterator
template<typename It>
int pass(It begin, It end) {
if (end - begin <= 1) return 0;
int result = 0;
auto it = begin;
while(++it != end) {
// We keep track of the current water level by simply querying the begin iterator.
if (*it <= *begin) {
result += *begin - *it;
} else {
// We need to define a new water level. Let's just do a new pass, with the begin pointing to the new column.
return result + pass(it, end);
}
}
// If we got here, it means we reached the end. We should discard the result, and do a reverse pass instead
return pass(std::reverse_iterator(end), std::reverse_iterator(begin));
}
Use it like this:
int main() {
std::vector<int> v{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1};
std::cout << pass(v.begin(), v.end());
}
// Output: 6
I am currently struggling with a homework problem for my Algorithms Class. A summary of the instruction:
The user enters an integer 'n' to determine the number of test cases.
The user individually enters another integer 'num' to determine the # of elements in each test case.
The user enters the elements of the individual array.
The algorithm has to process the array and determine whether it can be partitioned into two subsequences, each of which is in strictly increasing order. If the result is positive, the program prints "Yes", otherwise it prints "No".
I have 24 hours to complete this assignment but am struggling with the primary problem - I cannot properly process the user input. (come up with an algorithm to split the two subsequences)
update: I got to this solution. It passes 4/5 tests but fails the time constraint in the last test.
#include<iostream>
#include<string>
using namespace std;
bool run(){
int numbers;
int *arr;
cin >> numbers;
arr = new int[numbers];
for (int i = 0; i < numbers; i++)
cin >> arr[i];
long long int MAX = 0;
long long int MAX2 = 0;
string stra = "";
string strb = "";
string result = "";
string total = "";
long long int sum = 0;
for (int i = 0; i < numbers; i++){
if (arr[i] >= MAX && arr[i] != arr[i - 1]){
stra += to_string(arr[i]);
MAX = arr[i];
}
else
if (arr[i] >= MAX2 && MAX2 != MAX){
strb += to_string(arr[i]);
MAX2 = arr[i];
}
}
for (int i = 0; i < numbers; i++){
result = to_string(arr[i]);
total += result;
}
long long int len1 = stra.length();
long long int len2 = strb.length();
sum += len1 + len2;
delete[] arr;
if (sum != total.length())
return false;
else
return true;
}
int main()
{
int test;
cin >> test;
while (test > 0)
{
if (run())
cout << "Yes\n";
else
cout << "No\n";
test--;
}
system("pause");
}
Example input:
2
5
3 1 5 2 4
5
4 8 1 5 3
Example output:
Yes
No
Explanation: For the array 3 1 5 2 4, the two strictly increasing subsequences are: 3 5 and 1 2 4.
It seems that the existence of any equal or decreasing subsequence of at least three elements means the array cannot be partitioned into two subsequences, each with strictly increasing order, since once we've placed the first element in one part and the second element in the other part, we have no place to place the third.
This seems to indicate that finding the longest decreasing or equal subsequence is a sure solution. Since we only need one of length 3, we can record in O(n) for each element if it has a greater or equal element to the left. Then perform the reverse. If any element has both a greater or equal partner on the left and a smaller or equal partner on the right, the answer is "no."
We can visualise the O(n) time, O(1) space method by plotting along value and position:
A choosing list B here
A x would be wrong
x
value B z
^ B x
| x
| A
| x
|
| B
| x
- - - - - - - -> position
We notice that as soon as a second list is established (with the first decrease), any element higher than the absolute max so far must be assigned to the list that contains it, and any element lower than it can, in any case, only be placed in the second list if at all.
If we were to assign an element higher than the absolute max so far to the second list (that does not contain it), we could arbitrarily construct a false negative by making the next element lower than both the element we just inserted into the second list and the previous absolute max, but greater than the previous max of the second list (z in the diagram). If we had correctly inserted the element higher than the previous absolute max into that first list, we'd still have room to insert the new, arbitrary element into the second list.
(The JavaScript code below technically uses O(n) space in order to show the partition but notice that we only rely on the last element of each part.)
function f(A){
let partA = [A[0]];
let partB = [];
for (let i=1; i<A.length; i++){
if (A[i] > partA[partA.length-1])
partA.push(A[i]);
else if (partB.length && A[i] <= partB[partB.length-1])
return false;
else
partB.push(A[i]);
}
return [partA, partB];
}
let str = '';
let examples = [
[30, 10, 50, 25, 26],
[3, 1, 5, 2, 4],
[4, 8, 1, 5, 3],
[3, 1, 1, 2, 4],
[3, 4, 5, 1, 2],
[3, 4, 1],
[4, 1, 2, 7, 3]
];
for (e of examples)
str += JSON.stringify(e) + '\n' + JSON.stringify(f(e)) + '\n\n';
console.log(str);
I would go over the entire array once and check two maximal values. If the actual array value is smaller than both maxima, it is not possible, otherwise the proper maximum is increased.
The algorithm does not have to traverse the whole array, if the split condition is violated before.
Here is my code
#include <algorithm>
#include <iostream>
#include <vector>
bool isAddable(const int item, int &max1, int &max2) {
if (max2 > item) {
return false;
}
else {
if (max1 > item) {
max2 = item;
}
else {
max1 = item;
}
return true;
}
}
void setStartValue(int &max1, int &max2, const std::vector<int> &vec) {
max1 = *std::min_element(vec.begin(), vec.begin() + 3);
max2 = *std::max_element(vec.begin(), vec.begin() + 3);
}
bool isDiviableIntoTwoIncreasingArrays(const std::vector<int> &vec) {
if (vec.size() < 3) {
return true;
}
int max1, max2;
setStartValue(max1, max2, vec);
for (int i = 2; i < vec.size(); ++i) {
if (max1 > max2) {
if (!isAddable(vec[i], max1, max2)) {
return false;
}
}
else {
if (!isAddable(vec[i], max2, max1)) {
return false;
}
}
}
return true;
}
int main() {
std::vector<int> userVec;
int tmp1;
while (std::cin >> tmp1) {
userVec.emplace_back(tmp1);
}
const std::vector<int> v1{3, 1, 5, 2, 4};
const std::vector<int> v2{4, 8, 1, 5, 3};
const std::vector<int> v3{3, 4, 1};
for (const std::vector<int> &vec : {userVec, v1, v2, v3}) {
if (isDiviableIntoTwoIncreasingArrays(vec)) {
std::cout << "Yes\n";
}
else {
std::cout << "No\n";
}
}
}
I think you could resort to using a brute force solution. Notice here I use vectors(I think you should as well) to store the data and I use recursion to exhaust out all possible combinations. Keep the problem in mind, solve it and then focus on trivial tasks like parsing the input and matching the way your coursework expects you to enter data. I have added inline comments to make this understandable.
bool canPartition(vector<int>& nums) {
if(nums.empty()) return false;
vector<int> part1 = {}, part2 = {}; // two partitions
auto ans = canPart(nums, part1, part2, 0); // pass this to our recursive function
return ans;
}
bool canPart(vector<int>& nums, vector<int>& part1, vector<int>& part2, int i)
{
if(i >= nums.size()) // we are at the end of the array is this a solution?
{
if(!part1.empty() && !part2.empty()) // only if the partitions are not empty
{
//if you want you could print part1 and part2 here
//to see what the partition looks like
return true;
}
return false;
}
bool resp1empty = false, resp2empty = false, resp1 = false, resp2 = false;
if(part1.empty()) // first partition is empty? lets add something
{
part1.push_back(nums[i]);
resp1empty = canPart(nums, part1, part2, i + 1);
part1.pop_back(); // well we need to remove this element and try another one
}
else if(nums[i] > part1.back()) // first partition is not empty lets check if the sequence is increasing
{
part1.push_back(nums[i]);
resp1 = canPart(nums, part1, part2, i + 1);
part1.pop_back();
}
if(part2.empty()) // is partition two empty? lets add something
{
part2.push_back(nums[i]);
resp2empty = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
else if(nums[i] > part2.back()) // check if sequence is increasing
{
part2.push_back(nums[i]);
resp2 = canPart(nums, part1, part2, i + 1);
part2.pop_back();
}
//if any of the recursive paths returns a true we have an answer
return resp1empty || resp2empty || resp1 || resp2;
}
You can now try this out with a main function:
vector<int> v = {3,1,5,2,4};
cout << canPartition(v);
The key take away is make a small test case, add a few more non trivial test cases, solve the problem and then look into parsing inputs for other test cases
I think this comes down to whether you have an option for a number to appear in the first list or second list or not.
So, we will keep adding numbers to list 1 and if we can't add any element, we will make it as the start of the new list.
Let's say, we have both the lists going. If we come across an element to whom we can't add to any of the lists, we return false.
There does arise a situation where we could add an element to any of the 2 lists. In this scenario, we adopt a greedy approach as to add to which list.
We prepare an array of minimum values from the right. For example, for [30,10,50,25,26], we will have an array of minimums as [10,25,25,26,(empty here since last)].
Now, let's trace how we could divide them into 2 lists properly.
30 => List A.
10 => List B. (since you can't add it first list, so make a new one from here)
50 => List A.
Here, 50 applies to come after either 30 or 10. If we choose 10, then we won't be able to accommodate the next 25 in either of the 2 lists and our program would fail here itself, since our lists would look like [30] and [10,50]. However, we could continue further if we add 50 to 30 by checking for the minimum stored for it in our minimums array, which is 25.
25 => List B.
26 => List B.
So, our final lists are [30,50] and [10,25,26].
Time complexity: O(n), Space complexity: O(n) and you can print the 2 lists as well.
If we come across a sorted array which is strictly increasing, we return true for them anyway.
I have worked out a O(n square) solution to the problem. I was wondering about a better solution to this. (this is not a homework/interview problem but something I do out of my own interest, hence sharing here):
If a=1, b=2, c=3,….z=26. Given a string, find all possible codes that string
can generate. example: "1123" shall give:
aabc //a = 1, a = 1, b = 2, c = 3
kbc // since k is 11, b = 2, c= 3
alc // a = 1, l = 12, c = 3
aaw // a= 1, a =1, w= 23
kw // k = 11, w = 23
Here is my code to the problem:
void alpha(int* a, int sz, vector<vector<int>>& strings) {
for (int i = sz - 1; i >= 0; i--) {
if (i == sz - 1) {
vector<int> t;
t.push_back(a[i]);
strings.push_back(t);
} else {
int k = strings.size();
for (int j = 0; j < k; j++) {
vector<int> t = strings[j];
strings[j].insert(strings[j].begin(), a[i]);
if (t[0] < 10) {
int n = a[i] * 10 + t[0];
if (n <= 26) {
t[0] = n;
strings.push_back(t);
}
}
}
}
}
}
Essentially the vector strings will hold the sets of numbers.
This would run in n square. I am trying my head around at least an nlogn solution.
Intuitively tree should help here, but not getting anywhere post that.
Generally, your problem complexity is more like 2^n, not n^2, since your k can increase with every iteration.
This is an alternative recursive solution (note: recursion is bad for very long codes). I didn't focus on optimization, since I'm not up to date with C++X, but I think the recursive solution could be optimized with some moves.
Recursion also makes the complexity a bit more obvious compared to the iterative solution.
// Add the front element to each trailing code sequence. Create a new sequence if none exists
void update_helper(int front, std::vector<std::deque<int>>& intermediate)
{
if (intermediate.empty())
{
intermediate.push_back(std::deque<int>());
}
for (size_t i = 0; i < intermediate.size(); i++)
{
intermediate[i].push_front(front);
}
}
std::vector<std::deque<int>> decode(int digits[], int count)
{
if (count <= 0)
{
return std::vector<std::deque<int>>();
}
std::vector<std::deque<int>> result1 = decode(digits + 1, count - 1);
update_helper(*digits, result1);
if (count > 1 && (digits[0] * 10 + digits[1]) <= 26)
{
std::vector<std::deque<int>> result2 = decode(digits + 2, count - 2);
update_helper(digits[0] * 10 + digits[1], result2);
result1.insert(result1.end(), result2.begin(), result2.end());
}
return result1;
}
Call:
std::vector<std::deque<int>> strings = decode(codes, size);
Edit:
Regarding the complexity of the original code, I'll try to show what would happen in the worst case scenario, where the code sequence consists only of 1 and 2 values.
void alpha(int* a, int sz, vector<vector<int>>& strings)
{
for (int i = sz - 1;
i >= 0;
i--)
{
if (i == sz - 1)
{
vector<int> t;
t.push_back(a[i]);
strings.push_back(t); // strings.size+1
} // if summary: O(1), ignoring capacity change, strings.size+1
else
{
int k = strings.size();
for (int j = 0; j < k; j++)
{
vector<int> t = strings[j]; // O(strings[j].size) vector copy operation
strings[j].insert(strings[j].begin(), a[i]); // strings[j].size+1
// note: strings[j].insert treated as O(1) because other containers could do better than vector
if (t[0] < 10)
{
int n = a[i] * 10 + t[0];
if (n <= 26)
{
t[0] = n;
strings.push_back(t); // strings.size+1
// O(1), ignoring capacity change and copy operation
} // if summary: O(1), strings.size+1
} // if summary: O(1), ignoring capacity change, strings.size+1
} // for summary: O(k * strings[j].size), strings.size+k, strings[j].size+1
} // else summary: O(k * strings[j].size), strings.size+k, strings[j].size+1
} // for summary: O(sum[i from 1 to sz] of (k * strings[j].size))
// k (same as string.size) doubles each iteration => k ends near 2^sz
// string[j].size increases by 1 each iteration
// k * strings[j].size increases by ?? each iteration (its getting huge)
}
Maybe I made a mistake somewhere and if we want to play nice we can treat a vector copy as O(1) instead of O(n) in order to reduce complexity, but the hard fact remains, that the worst case is doubling outer vector size in each iteration (at least every 2nd iteration, considering the exact structure of the if conditions) of the inner loop and the inner loop depends on that growing vector size, which makes the whole story at least O(2^n).
Edit2:
I figured out the result complexity (the best hypothetical algoritm still needs to create every element of the result, so result complexity is like a lower bound to what any algorithm can archieve)
Its actually following the Fibonacci numbers:
For worst case input (like only 1s) of size N+2 you have:
size N has k(N) elements
size N+1 has k(N+1) elements
size N+2 is the combination of codes starting with a followed by the combinations from size N+1 (a takes one element of the source) and the codes starting with k, followed by the combinations from size N (k takes two elements of the source)
size N+2 has k(N) + k(N+1) elements
Starting with size 1 => 1 (a) and size 2 => 2 (aa or k)
Result: still exponential growth ;)
Edit3:
Worked out a dynamic programming solution, somewhat similar to your approach with reverse iteration over the code array and kindof optimized in its vector usage, based on the properties explained in Edit2.
The inner loop (update_helper) is still dominated by the count of results (worst case Fibonacci) and a few outer loop iterations will have a decent count of sub-results, but at least the sub-results are reduced to a pointer to some intermediate node, so copying should be pretty efficient. As a little bonus, I switched the result from numbers to characters.
Another edit: updated code with range 0 - 25 as 'a' - 'z', fixed some errors that led to wrong results.
struct const_node
{
const_node(char content, const_node* next)
: next(next), content(content)
{
}
const_node* const next;
const char content;
};
// put front in front of each existing sub-result
void update_helper(int front, std::vector<const_node*>& intermediate)
{
for (size_t i = 0; i < intermediate.size(); i++)
{
intermediate[i] = new const_node(front + 'a', intermediate[i]);
}
if (intermediate.empty())
{
intermediate.push_back(new const_node(front + 'a', NULL));
}
}
std::vector<const_node*> decode_it(int digits[9], size_t count)
{
int current = 0;
std::vector<const_node*> intermediates[3];
for (size_t i = 0; i < count; i++)
{
current = (current + 1) % 3;
int prev = (current + 2) % 3; // -1
int prevprev = (current + 1) % 3; // -2
size_t index = count - i - 1; // invert direction
// copy from prev
intermediates[current] = intermediates[prev];
// update current (part 1)
update_helper(digits[index], intermediates[current]);
if (index + 1 < count && digits[index] &&
digits[index] * 10 + digits[index + 1] < 26)
{
// update prevprev
update_helper(digits[index] * 10 + digits[index + 1], intermediates[prevprev]);
// add to current (part 2)
intermediates[current].insert(intermediates[current].end(), intermediates[prevprev].begin(), intermediates[prevprev].end());
}
}
return intermediates[current];
}
void cleanupDelete(std::vector<const_node*>& nodes);
int main()
{
int code[] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
int size = sizeof(code) / sizeof(int);
std::vector<const_node*> result = decode_it(code, size);
// output
for (size_t i = 0; i < result.size(); i++)
{
std::cout.width(3);
std::cout.flags(std::ios::right);
std::cout << i << ": ";
const_node* item = result[i];
while (item)
{
std::cout << item->content;
item = item->next;
}
std::cout << std::endl;
}
cleanupDelete(result);
}
void fillCleanup(const_node* n, std::set<const_node*>& all_nodes)
{
if (n)
{
all_nodes.insert(n);
fillCleanup(n->next, all_nodes);
}
}
void cleanupDelete(std::vector<const_node*>& nodes)
{
// this is like multiple inverse trees, hard to delete correctly, since multiple next pointers refer to the same target
std::set<const_node*> all_nodes;
for each (auto var in nodes)
{
fillCleanup(var, all_nodes);
}
nodes.clear();
for each (auto var in all_nodes)
{
delete var;
}
all_nodes.clear();
}
A drawback of the dynamically reused structure is the cleanup, since you wanna be careful to delete each node only once.
I have done a test in C++ asking for a function that returns one of the indices that splits the input vector in 2 parts having the same sum of the elements, for eg: for the vec = {1, 2, 3, 5, 4, -1, 1, 1, 2, -1}, it may return 3, because 1+2+3 = 6 = 4-1+1+1+2-1. So I have done the function that returns the correct answer:
int func(const std::vector< int >& vecIn)
{
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (std::accumulator(vecIn.begin(), vecIn.begin() + p, 0) ==
std::accumulator(vecIn.begin() + p + 1, vecIn.end(), 0))
return p;
}
return -1;
}
My problem was when the input was a very long vector containing just 1 (or -1), the return of the function was slow. So I have thought of starting the search for the wanted index from middle, and then go left and right. But the best approach I suppose is the one where the index is in the merge-sort algorithm order, that means: n/2, n/4, 3n/4, n/8, 3n/8, 5n/8, 7n/8... where n is the size of the vector. Is there a way to write this order in a formula, so I can apply it in my function?
Thanks
EDIT
After some comments I have to mention that I had done the test a few days ago, so I have forgot to put and mention the part of no solution: it should return -1... I have updated also the question title.
Specifically for this problem, I would use the following algorithm:
Compute the total sum of the vector. This gives two sums (empty vector, and full vector)
for each element in order, move one element from full to empty, which means adding the value of next element from sum(full) to sum(empty). When the two sums are equal, you have found your index.
This give a o(n) algorithm instead of o(n2)
You can solve the problem much faster without calling std::accumulator at each step:
int func(const std::vector< int >& vecIn)
{
int s1 = 0;
int s2 = std::accumulator(vecIn.begin(), vecIn.end(), 0);
for (std::size_t p = 0; p < vecin.size(); p++)
{
if (s1 == s2)
return p;
s1 += vecIn[p];
s2 -= vecIn[p];
}
}
This is O(n). At each step, s1 will contain the sum of the first p elements, and s2 the sum of the rest. You can update both of them with an addition and a subtraction when moving to the next element.
Since std::accumulator needs to iterate over the range you give it, your algorithm was O(n^2), which is why it was so slow for many elements.
To answer the actual question: Your sequence n/2, n/4, 3n/5, n/8, 3n/8 can be rewritten as
1*n/2
1*n/4 3*n/4
1*n/8 3*n/8 5*n/8 7*n/8
...
that is to say, the denominator runs from i=2 up in powers of 2, and the nominator runs from j=1 to i-1 in steps of 2. However, this is not what you need for your actual problem, because the example you give has n=10. Clearly you don't want n/4 there - your indices have to be integer.
The best solution here is to recurse. Given a range [b,e], pick a value middle (b+e/2) and set the new ranges to [b, (b+e/2)-1] and [(b+e/2)=1, e]. Of course, specialize ranges with length 1 or 2.
Considering MSalters comments, I'm afraid another solution would be better. If you want to use less memory, maybe the selected answer is good enough, but to find the possibly multiple solutions you could use the following code:
static const int arr[] = {5,-10,10,-10,10,1,1,1,1,1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
// compute cumulative sum
std::vector<int> cumulative_sum( vec.size() );
cumulative_sum[0] = vec[0];
for ( size_t i = 1; i < vec.size(); i++ )
{ cumulative_sum[i] = cumulative_sum[i-1] + vec[i]; }
const int complete_sum = cumulative_sum.back();
// find multiple solutions, if there are any
const int complete_sum_half = complete_sum / 2; // suggesting this is valid...
std::vector<int>::iterator it = cumulative_sum.begin();
std::vector<int> mid_indices;
do {
it = std::find( it, cumulative_sum.end(), complete_sum_half );
if ( it != cumulative_sum.end() )
{ mid_indices.push_back( it - cumulative_sum.begin() ); ++it; }
} while( it != cumulative_sum.end() );
for ( size_t i = 0; i < mid_indices.size(); i++ )
{ std::cout << mid_indices[i] << std::endl; }
std::cout << "Split behind these indices to obtain two equal halfs." << std::endl;
This way, you get all the possible solutions. If there is no solution to split the vector in two equal halfs, mid_indices will be left empty.
Again, you have to sum up each value only once.
My proposal is this:
static const int arr[] = {1,2,3,5,4,-1,1,1,2,-1};
std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
int idx1(0), idx2(vec.size()-1);
int sum1(0), sum2(0);
int idxMid = -1;
do {
// fast access without using the index each time.
const int& val1 = vec[idx1];
const int& val2 = vec[idx2];
// Precompute the next (possible) sum values.
const int nSum1 = sum1 + val1;
const int nSum2 = sum2 + val2;
// move the index considering the balanace between the
// left and right sum.
if ( sum1 - nSum2 < sum2 - nSum1 )
{ sum1 = nSum1; idx1++; }
else
{ sum2 = nSum2; idx2--; }
if ( idx1 >= idx2 ){ idxMid = idx2; }
} while( idxMid < 0 && idx2 >= 0 && idx1 < vec.size() );
std::cout << idxMid << std::endl;
It does add every value only once no matter how many values. Such that it's complexity is only O(n) and not O(n^2).
The code simply runs from left and right simultanuously and moves the indices further if it's side is lower than the other.
You want nth term of the series you mentioned. Then it would be:
numerator: (n - 2^((int)(log2 n)) ) *2 + 1
denominator: 2^((int)(log2 n) + 1)
I came across the same question in Codility tests. There is a similar looking answer above (didn't pass some of the unit tests), but below code segment was successful in tests.
#include <vector>
#include <numeric>
#include <iostream>
using namespace std;
// Returns -1 if equilibrium point is not found
// use long long to support bigger ranges
int FindEquilibriumPoint(vector<long> &values) {
long long lower = 0;
long long upper = std::accumulate(values.begin(), values.end(), 0);
for (std::size_t i = 0; i < values.size(); i++) {
upper -= values[i];
if (lower == upper) {
return i;
}
lower += values[i];
}
return -1;
}
int main() {
vector<long> v = {-1, 3, -4, 5, 1, -6, 2, 1};
cout << "Equilibrium Point:" << FindEquilibriumPoint(v) << endl;
return 0;
}
Output
Equilibrium Point:1
Here it is the algorithm in Javascript:
function equi(arr){
var N = arr.length;
if (N == 0){ return -1};
var suma = 0;
for (var i=0; i<N; i++){
suma += arr[i];
}
var suma_iz = 0;
for(i=0; i<N; i++){
var suma_de = suma - suma_iz - arr[i];
if (suma_iz == suma_de){
return i};
suma_iz += arr[i];
}
return -1;
}
As you see this code satisfy the condition of O(n)