Related
The general problem:
Given an array of integers nums and an integer k. A continuous subarray is called nice if there are k odd numbers on it.
Return the number of nice sub-arrays.
My question:
I want to solve this problem recursively. The part I'm struggling to implement is how do I use the count from every previous recursive call and sum up all the counts, and return one last count like it is shown in the attempt.
Example:
Input: nums = [1,1,2,1,1], k = 3
Output: 2
Explanation: The only sub-arrays with 3 odd numbers are [1,1,2,1] and [1,2,1,1].
My attempt:
int helperFunction(vector<int> &nums, int k, int starter, int count)
{
int sum=0;
if (starter >= nums.size())
{
return count;
}
for (int i = starter; i < nums.size(); i++)
{
if (nums[i] % 2 == 1)
{
if (++sum == k)
{
count += nums.size() - i;
sum = 0;
}
}
}
return helperFunction(nums, k, starter + 1, count);
}
int numberOfSubarrays(vector<int> &nums, int k)
{
return helperFunction(nums, k, 0, 0);
}
int main()
{
vector<int> mine;
int myints[] = {1, 1, 2, 1, 1};
mine.assign(myints, myints + 5);
cout << "Output : " << numberOfSubarrays(mine, 3);
return 0;
}
Return Value:
The return value of this actual attempt is 0 means the program is at least not wrong syntactically.
it's not particularly good candidate for recursion. might be possible to solve with just one pass on the array.
That said, small adjustments to your code could make it work. There is no reason to pass count into the recursive method.
Your method calculates the number of subarrays that are 'nice' starting with the given index. Add that to the number that start at the next index and return it.
int helperFunction(vector<int> &nums, int k, int starter)
{
int sum=0, count=0;
if (starter >= nums.size())
{
return 0;
}
for (int i = starter; i < nums.size() && sum <= k; i++)
{
if (nums[i] % 2 == 1)
{
sum++;
}
if (sum == k)
{
count++;
}
}
return helperFunction(nums, k, starter + 1) + count;
}
I'm not sure your counting was correct. This could be optimized a lot, but this should demostrate the recursive approach.
For example I have this set k=5 of elements [1,2,3,4,5] and I want all permutations of length n=2.
1,2
1,3
1,4
1,5
2,1
etc etc.
Thing is I can't use STL, external math libraries etc.
What I tried is generating all permutations of all the elements using Heap's algorithm, and then all the permutations of n elements where contained in the first n numbers of all k-permutations and I could just truncate and delete duplicates, but then the complexity is way too high(n!)
I know the problem has a good solution as I've seen this being done with extra modules/libraries in questions about permutating strings.
Extra info: I only need this to brute force an unbalanced assignment problem, and Hungarian algorithm seems way too long when I'm allowed to "brute-force" the problem. My approach didn't come close to the allowed execution time because when I have an array of for example size 8x3, my algorithm needs 8! comparisons when it definitely could be optimized to a much smaller number.
I think you can do it in two steps, first, generate combination of k elements out of a set of n, then print permutation of each combination. I tested this code and works fine:
#include <iostream>
using namespace std;
void printArr(int a[], int n, bool newline = true) {
for (int i=0; i<n; i++) {
if (i > 0) cout << ",";
cout << a[i];
}
if (newline) cout << endl;
}
// Generating permutation using Heap Algorithm
void heapPermutation(int a[], int n, int size) {
// if size becomes 1 then prints the obtained permutation
if (size == 1) {
printArr(a, n);
return;
}
for (int i=0; i<size; i++) {
heapPermutation(a, n, size-1);
// if size is odd, swap first and last element, otherwise swap ith and last element
swap(a[size%2 == 1 ? 0 : i], a[size-1]);
}
}
// Generating permutation using Heap Algorithm
void heapKPermutation(int a[], int n, int k, int size) {
// if size becomes 1 then prints the obtained permutation
if (size == n - k + 1) {
printArr(a + n - k, k);
return;
}
for (int i=0; i<size; i++) {
heapKPermutation(a, n, k, size-1);
// if size is odd, swap first and last element, otherwise swap ith and last element
swap(a[size%2 == 1 ? 0 : i], a[size-1]);
}
}
void doKCombination(int a[], int n, int p[], int k, int size, int start) {
int picked[size + 1];
for (int i = 0; i < size; ++i) picked[i] = p[i];
if (size == k) {
// We got a valid combination, use the heap permutation algorithm to generate all permutations out of it.
heapPermutation(p, k, k);
} else {
if (start < n) {
doKCombination(a, n, picked, k, size, start + 1);
picked[size] = a[start];
doKCombination(a, n, picked, k, size + 1, start + 1);
}
}
}
// Generate combination of k elements out of a set of n
void kCombination(int a[], int n, int k) {
doKCombination(a, n, nullptr, k, 0, 0);
}
int main()
{
int a[] = {1, 2, 3, 4, 5};
cout << "n=1, k=1, a=";
printArr(a, 1);
kCombination(a, 1, 1);
cout << "n=2, k=1, a=";
printArr(a, 2);
kCombination(a, 2, 1);
cout << "n=3, k=2, a=";
printArr(a, 3);
kCombination(a, 3, 2);
cout << "n=5, k=2, a=";
printArr(a, 5);
kCombination(a, 5, 2);
return 0;
}
The result is:
n=1, k=1, a=1
1
n=2, k=1, a=1,2
2
1
n=3, k=2, a=1,2,3
2,3
3,2
1,3
3,1
1,2
2,1
n=5, k=2, a=1,2,3,4,5
4,5
5,4
3,5
5,3
3,4
4,3
2,5
5,2
2,4
4,2
2,3
3,2
1,5
5,1
1,4
4,1
1,3
3,1
1,2
2,1
In practice, you have k possibilities for the first value.
Then, once you have selected this first value, the problem is to generate all permutations with n-1 and k-1 parameters.
This lead to a rather simple recursive implementation. There may be faster methods. However, it is clearly faster than your algorithm.
#include <iostream>
#include <algorithm>
bool Pnk (int n, int k, int *a, int iter, int offset) {
if (n == 0) {
return false;
}
bool check = true;
int index = 0;
std::swap (a[iter], a[iter+offset]);
while (check) {
if (n-1 == 0) {
for (int i = 0; i <= iter; ++i) {
std::cout << a[i] << " ";
}
std::cout << "\n";
}
check = Pnk (n-1, k-1, a, iter + 1, index);
index++;
}
std::swap (a[iter], a[iter+offset]);
return offset != k-1;
}
void Pnk0 (int n, int k, int *a) {
int offset = 0;
while (Pnk (n, k, a, 0, offset)) {
offset++;
}
}
int main () {
int length = 3;
const int size = 4;
int a[size] = {1, 2, 3, 4};
Pnk0 (length, size, a);
}
If you don't care about output being in lexicographic order here's a fairly straightforward implementation.
using namespace std;
void perm(int* a, int n, int k, int i)
{
if(i == 0)
{
for(int j=n; j<n+k; j++) cout << a[j] << " ";
cout << endl;
return;
}
for(int j=0; j<n; j++)
{
swap(a[j], a[n-1]);
perm(a, n-1, k, i-1);
swap(a[j], a[n-1]);
}
}
Test (OnlineGDB):
int n = 4, k = 2;
int a[] = {1,2,3,4};
perm(a, n, k, k);
Output:
4 1
2 1
3 1
1 2
4 2
3 2
1 3
2 3
4 3
1 4
2 4
3 4
I don't know if n is always 2, but if it is and you don't mind performance, here's some """working""" code:
#include <iostream>
#include <tuple>
#include <vector>
std::vector<std::tuple<int, int> >
get_tuples_from_vector (const std::vector<int> elements)
{
std::vector<std::tuple<int, int> > tuples;
for (int i = 0; i < elements.size(); ++i)
{
for (int j = i + 1; j < elements.size(); ++j)
{
tuples.push_back({elements[i], elements[j]});
}
}
return tuples;
}
std::vector<std::tuple<int, int> >
generate_permutations (const std::vector<int>& elements)
{
if (elements.size() == 0 || elements.size() < 2)
{
return {};
}
std::vector<std::tuple<int, int> > tuples
{
get_tuples_from_vector(elements)
};
std::vector<std::tuple<int, int> > permutations;
for (const auto& tuple: tuples)
{
permutations.push_back(
{ std::get<0>(tuple), std::get<1>(tuple) }
);
permutations.push_back(
{ std::get<1>(tuple), std::get<0>(tuple) }
);
}
return permutations;
}
void print_vector(std::vector<std::tuple<int, int> > elements)
{
for (const auto& element: elements)
{
std::cout << "[ "
<< std::get<0>(element)
<< ", " << std::get<1>(element)
<< "]\n";
}
std::cout << std::endl;
}
int main(int argc, char const *argv[])
{
print_vector(generate_permutations({1, 2, 3, 4, 5}));
}
I have a question regarding the implementation of an algorithm using Divide and conquer.
So, we have an unsorted array V[] and we have to find the k element of the array as if the array is sorted but without completely sort the array v.
Example:
Size of array = 8;
k = 3;
For example, if k = 3 and v = {2, 7, 5, 9, 8, 10, 3, 4} the k element of that array is 5.
Obs: The array is indexed from 0 to size - 1.
My code:
void read_STL(std::vector<int>& vect,int &k)
{
std::ifstream fisier("file.txt");
int dim;
fisier >> dim;
fisier >> k;
for (int i = 0; i < dim; i++)
{
int elem;
fisier >> elem;
vect.push_back(elem);
}
fisier.close();
}
bool criteriu(int i, int j)
{
return (i < j);
}
int search_DQ(std::vector<int>& vect, int st, int dr,int k)
{
if (st == dr)
{
return vect[st];
}
int mijl = (st + dr) / 2;
if (k == mijl)
{
return vect[k];
}
else if (k < mijl)
{
return search_DI(vect, st, mijl - 1, k);
}
else
{
return search_DQ(vect, mijl + 1, dr, k);
}
}
int main()
{
std::vector<int>vect;
int st, dr, k;
read_STL(vect,k);
st = 0; dr = vect.size() - 1;
std::cout<<search_DQ(vect, st, dr,k);
return 0;
}
So I have an algorithm that is suppose to return the int that appears K times in an array. If more than 1 int appears K times, the higher value should be returned. My following algorithm is not working correctly. In the example below, it returns 1 when it should be returning 5.
#include <iostream>
#include <algorithm>
int appearsKTimes (int size, int inputArray[], int k) {
std::sort(inputArray, inputArray + size);
int i = 1, count = 1;
int element = inputArray[0];
int res = -1;
while (i < size) {
if (element == inputArray[i]) {
count++;
} else {
if (count == k) {
res = element;
}
element = inputArray[i];
count = 1;
}
i++;
}
std::cout << res << std::endl;
return res;
}
int main() {
const int size = 7;
int array[size] = {1, 1, 2, 2, 2, 5, 5};
int occurences = 2;
appearsKTimes(size, array, occurences);
}
if (count == k) {
res = element;
}
std::cout << res << std::endl;
check the count of the last element.
I also think it will be better to count from the end.You need to check the all array all the time if you count from the first element.
Your approach with sorting is good and requires only O(NlogN) time complexity, but consider an another one with hash table as well. It requires O(N) in time and O(N) in space. It is shorter and has only a few extra variables, so there is less chance of making mistake:
1) Compute number frequencies with unordered_map (hash table): O(N)
2) Find the largest number with exactly k occurences: O(N)
int appearsKTimes (int size, int inputArray[], int k) {
unordered_map<int, int> freqs;
for (int i = 0; i < size; i++) {
freqs[inputArray[i]]++;
}
int res = - 1;
for (int i = 0; i < size; i++) {
if (freqs[inputArray[i]] == k)
res = max(res, inputArray[i]);
}
return res;
}
The problem is to find the number of times a subset of the numbers in an array add up to a specific target number.
For example, there are two ways to partition the set {1, 3, 4, 5} so that the remaining elements add up to 5:
Select the 1 and the 4
Select just the 5
By contrast, there is no way to partition the set {1, 3, 4, 5} to get 11.
#include "genlib.h"
#include <iostream>
void RecursePart(int[], int, int, int&);
int Wrapper(int[], int, int);
int main() {
int arr[8] = {1,2,3,4,5,6,7,8};
cout << Wrapper(arr, 8, 11);
}
void RecursePart(int arr[], int len, int target, int& ctr) {
if (len == 1) {
if (arr[0] == target) {
ctr++;
}
return;
}
int sum, temp;
sum = temp = arr[0];
for (int j = 1; j < len; j++) {
if (sum == target) {
ctr++;
break;
}
sum = sum + arr[j];
if (sum == target) {
ctr++;
sum = temp;
continue;
}
if (sum > target) {
sum = temp;
continue;
}
if (sum < target) {
temp = sum;
continue;
}
}
RecursePart(arr + 1, len - 1, target, ctr);
}
int Wrapper(int arr[], int len, int target) {
int n = 0;
RecursePart(arr, len, target, n);
return n;
}
The problem is that the output I get is 1 but the number of times a subset of the numbers in the array that add up to 11 is greater than just 1. I have tried to trace the algorithm and I know that the problem must be in the for loop. There the algorithm skips some sums. How could I override this problem?
Like others stated, this is the Subset Sum problem (which is NP-complete), meaning you need an exponential time algorithm to solve it.
Just by looking at your function, You call RecursePart once, each time with len-1, and then have a for-loop of length n, which means your computation is O(n^2). This obviously won't solve an O(2^n) problem.
The following is a Recursive solution that creates the sum of subsets, and tries to see if they reached the target. If there is no option for the current subset to equal target, the "creation" of the current subset is stopped.
int RecursePart(int arr[], int len, int idx, int curr_sum, int target)
{
int count = 0;
// this subset is good
if (curr_sum == target)
return 1;
// the sum of the current subset exceeds target, no point in continuing
if (curr_sum > target || idx == len)
return 0;
count += RecursePart(arr, len, idx+1, curr_sum + arr[idx], target);
count += RecursePart(arr, len, idx+1, curr_sum, target);
return count;
}
This is my previous solution, which creates all possible subsets, and the ones that match the target are counted.
#include <iostream>
int Wrapper(int[], int, int);
int main() {
int arr[8] = {1,2,3,4,5,6,7,8};
std::cout << Wrapper(arr, 8, 11);
}
// counts the sum of a subset
int CountSet(int* arr, int* mask, int len)
{
int sum = 0;
for (int i=0; i < len; ++i)
{
if (mask[i])
{
sum += arr[i];
}
}
return sum;
}
int RecursePart(int arr[], int idx, int len, int* subset_mask, int target)
{
int count = 0;
if (idx == len)
{
if (CountSet(arr, subset_mask, len) == target)
return 1;
else
return 0;
}
// create the subset "without" me
subset_mask[idx] = 0;
count += RecursePart(arr, idx+1, len, subset_mask, target);
// now create the subset "with" me
subset_mask[idx] = 1;
count += RecursePart(arr, idx+1, len, subset_mask, target);
return count;
}
int Wrapper(int arr[], int len, int target) {
int* subset_mask = (int*)malloc(len*sizeof(int));
int res = RecursePart(arr, 0, len, subset_mask, target);
free(subset_mask);
return res;
}
Your function is simply wrong. You can see the same error with Wrapper(arr,4,5). You sum up elements from the left as long as they don't exceed the target, so that you can't find a solution which involves only some of those elements. You must rethink the algorithm.
Since you're using C++, you an also use a vector<int> to store are the intermediate solutions like so:
#include <iostream>
#include <vector>
using namespace std;
vector<int> RecursePart(int[], int, int, int&);
int Wrapper(int[], int, int);
int main() {
int arr[8] = {8,2,3,4,5,6,7,1};
cout << Wrapper(arr, 8, 11);
}
vector<int> RecursePart(int arr[], int len, int target, int& ctr)
{
vector<int> return_vec;
if (len == 1)
{
if (arr[0] == target)
{
ctr++;
return return_vec;
}
return_vec.push_back(arr[0]);
return return_vec;
}
int current = arr[0];
if (current == target)
ctr++;
if (current < target)
return_vec.push_back(current);
vector<int> temp;
temp = RecursePart(arr + 1, len - 1, target, ctr);
for (int i = 0; i < temp.size(); i ++)
{
if (temp[i] + current == target)
{
ctr++;
}
if (temp[i] + current < target)
return_vec.push_back(temp[i] + current);
if (temp[i] < target)
return_vec.push_back(temp[i]);
}
/*Debug Print
cout << "Current: " << current << endl;
for (int i = 0 ; i < return_vec.size(); i++)
{
cout << return_vec[i] << ", ";
}
cout << endl;
*/
return return_vec;
}
int Wrapper(int arr[], int len, int target) {
int n = 0;
RecursePart(arr, len, target, n);
return n;
}
To elaborate more, what's happening is we are using recursion to decompose the list into it's simplest part, which is a single number (the last element of the list), and make our return type all the possible sums that are less than our target. In the base-case, which is basically the last element of the array, if that number is equal to the target, we increment the counter, and return an empty vector, since the return-vector, which is all the possible sums less than our target, shouldn't have an element equal to or greater than our target. In all the previous recursive steps to the base-case, we get the returned vector, and then compare the current value, to all the elements in the return vector, which again, represent all the possible sums up to that point that are less than the target. We check to see if there are any sums that match the target, and then copy over any sums to our return vector that are less than the target. We then move back to the previous recursive step.
The complexity of this algorithm is still exponential ... if you enable the debug print section, you will see how the growth of the length of the vector for each iteration will grow in an exponential manner, and therefore for a given set of size N, the number of solutions you will have to check for will be in-line with O(2^N). This version of the solution though pairs that back a bit though by making sure that we only move valid solutions onto the next iteration, avoiding the need to calculate every possible subset.