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.
Related
I am doing the coin problem, the problem says that,
Given a set of coin values coins = {c1, c2,..., ck} and a target sum
of money n, our task is to form the sum n using as few coins as
possible.
suppose you have 9 dollars and you have set of {6,5,1} so, the minimum no. of sum/change for 9 dollars would be ( 6+1+1+1=9) i.e. 4.
i tried doing it recursively using this formula :
solve(x) = min( solve(x−6)+1, solve(x−5)+1, solve(x−1)+1 )
,but I don't know why I'm getting Segmentation fault in my code.
There are plenty of codes available online, but I want to know what am I doing wrong here, I'm new to recursion please help me, The code goes here:
//my code
#include<bits/stdc++.h>
using namespace std;
int solve (int x, int a[], int n)
{
if (x < 0)
{
return INT_MAX;
}
if (x == 0)
{
return 0;
}
int best = INT_MAX;
for (int i = 0; i < n; i++)
{
best = min (best, solve (x - a[i], a, n) + 1);
}
return best;
}
int main ()
{
int a[] = { 6, 5, 1 };
int x = 9;
int n = 3;
cout << solve (x, a, n);
return 0;
}
The code which have been took from: https://www.geeksforgeeks.org/find-minimum-number-of-coins-that-make-a-change/
#include <iostream>
using namespace std;
int minCoins(int coins[], int m, int amount) {
if (amount == 0) return 0;
int res = INT_MAX;
for (int i = 0; i < m; i++) {
if (coins[i] <= amount) {
int sub_res = minCoins(coins, m, amount - coins[i]);
if (sub_res != INT_MAX && sub_res + 1 < res) { // avoid overflow
res = sub_res + 1;
}
}
}
return res;
}
int main() {
int coins[] = { 6, 5, 1 };
int amount = 9;
cout << "Min coins is "
<< minCoins(coins, sizeof(coins) / sizeof(coins[0]), amount)
<< endl;
return 0;
}
About the problem:
Your Segmentation fault comes from the line:
best = min (best, solve (x - i, a, n) + 1);
The reason is: x-i will always gives you the same value so if you are run the program without debugging, your program crashing. So don't try to debug it because it will takes a lot of time to see this crashing.
For starters change to: best = min (best, solve (x - a[i], a, n) + 1);.
After fixing the section 1, the if case: if (x < 0) return INT_MAX; will causes problem and will return always the same value, which is: -INT_MAX. So you need to check the "if cases" again.
The algorithm you try to implement is not correct, see the pseudo-code of this algorithm:
minchange(M):
if M = 0:
return 0
v <- infinity
for c in denominations <= M:
v <- min { minchange(M - c) + 1, v }
return v
Better use: sizeof(a) / sizeof(a[0]) instead of int n = 3.
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;
}
I want to enumerate all possible subsets of a given array, which should be 2^N in total. Here I use sum to represent all subsets since sums are distinct in this example. I use a auxiliary array called taken to mark if the current item is taken and do a DFS. Here is the code:
void dfs(vector<int>& res, vector<int>& nums, vector<bool>& taken, int sum, int pos) {
for (int i = pos; i < nums.size(); ++i) {
if (!taken[i]) {
taken[i] = true;
res.push_back(nums[i] + sum);
dfs(res, nums, taken, sum + nums[i], pos + 1);
taken[i] = false;
}
}
return;
}
int main() {
vector<int> test = {1, 10, 100, 1000};
vector<bool> t(4, false);
vector<int> res;
dfs(res, test, t, 0, 0);
return 0;
}
But this code does not return 2^n results in result.
I have fixed your code
void dfs(vector<int>& res, vector<int>& nums, int sum, int pos) {
for (int i = pos; i < nums.size(); ++i) {
res.push_back(sum + nums[i]);
dfs(res, nums, sum + nums[i], i + 1); // i + 1 instead of pos + 1
}
return;
}
int main() {
vector<int> test = {1, 2, 3};
vector<int> res;
dfs(res,test,0, 0);
cout << res.size() << endl;
copy(res.begin(), res.end(), ostream_iterator<int>(cout , " "));
cout <<endl;
return 0;
}
And since you do not visit the elements backwards (i.e. already added indexes) you do not have to maintain an array to check for visited.
void enumerate_all_subsets(vector <int> &res, const vector <int> &nums) {
if(nums.empty()) return;
for(auto i = 0ull; i < (1 << nums.size()); ++ i) {
int sum = 0;
for(auto k = 0; k < nums.size(); ++ k)
if((i >> k) & 1)
sum += nums[k];
res.push_back(sum);
}
}
This iterates over a vector of a size up to size 63 (if your unsigned long longs are 64 bit). Any larger vectors should probably be rethought as that would take a while as this is O(2^n), as you noted.
Basically each bit of the unsigned long long i represents a position in nums and if it should be added to the sum or not.
A zero-indexed array A consisting of N different integers is given. The array contains integers in the range [1..(N + 1)], which means that exactly one element is missing.
Your goal is to find that missing element.
Write a function:
int solution(int A[], int N);
that, given a zero-indexed array A, returns the value of the missing element.
For example, given array A such that:
A[0] = 2 A[1] = 3 A[2] = 1 A[3] = 5
the function should return 4, as it is the missing element.
Assume that:
N is an integer within the range [0..100,000];
the elements of A are all distinct;
each element of array A is an integer within the range [1..(N + 1)].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
It doesn't work for a case that there are two elements
int solution(vector<int> &A) {
sort(A.begin(), A.end());
int missingIndex = 0;
for (int i = 0; i < A.size(); i++)
{
if ( i != A[i]-1)
{
missingIndex = i+1;
}
}
return missingIndex;
}
Since your array is zero-indexed and the numbers are from 1 to N+1, the statement should be:
if ( i != A[i]-1)
Also, you should immediately break out from the for loop after updating the missingIndex because all entries beyond the missing element shall have (i != A[i]-1)
Moreover because of sorting your solution is O(NlogN) and not O(N).
Instead you can sum all the elements in the array (using unsigned long long int) and check its difference from N(N+1)/2
You can use the simple math formula for an arithmetic progression to get the sum of all numbers from 1 to N+1. Then iterate over all the given numbers and calculate that sum. The missing element will be the difference between the two sums.
int solution(std::vector<int> &a) {
uint64_t sum = (a.size() +1 ) * (a.size() + 2) / 2;
uint64_t actual = 0;
for(int element : a) {
actual += element;
}
return static_cast<int>(sum - actual);
}
Use all the power of STL:
#include <algorithm>
#include <functional>
int solution(vector<int> &A) {
return std::accumulate(A.begin(), A.end(), (A.size()+1) * (A.size()+2) / 2, std::minus<int>());
}
This solution uses the sign of the values as a flag. It needs at worst two pass over the elements. The N(N+1)/2 solution needs exactly one pass.
int solution(vector<int> &a) {
int n = (int)a.size();
for(auto k : a)
{
int i = abs(k) - 1;
if (i != n)
a[i] = -a[i];
}
for (int i = 0; i < n; ++i)
if (a[i]>0)
return i+1;
return n+1;
}
I solved it this way and thought of posting it here for my own reference for future and for others :)
#include <cstdint>
#include <numeric>
int solution(vector<int> &A) {
uint64_t sumAll = (A.size() + 1) * (A.size() + 2) / 2;
uint64_t sumA = std::accumulate(A.begin(), A.end(), 0);
return sumAll- sumA;
}
I solve it with this solution maybe there is something better but I test it with different values and find it work fine while the other solutions gives me strange results.
as example:
std::vector<int> A = { 12,13,11,14,16 };
std::vector<int> A2 = { 112,113,111,114,116 };
int Solution(std::vector<int> &A)
{
int temp;
for (int i = 0; i < A.size(); ++i)
{
for (int j = i+1;j< A.size();++j )
{
if (A[i] > A[j])
{
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
}
for (int i = 0; i < A.size()-1;++i)
{
if ((A[i] + 1 != A[i + 1]))
{
return (A[i] + 1);
}
if(i+1 == A.size() - 1)
return (A[i+1] + 1);
}}
Now Everything fine but if I use the array above with the methods below, I will get wrong values excepts with small numbers <10;
std::vector<int> A = { 12,13,11,14,16 };
int Solution_2(std::vector<int> &A)
{
unsigned int n = A.size() + 1;
long long int estimated = n * (n + 1) / 2;
long long int total = 0;
for (unsigned int i = 0; i < n - 1; i++) total += A[i];
return estimated - total;
}
I will get this result -45.
or this one also the same result if I use array A :
std::vector<int> A = { 12,13,11,14,16 };
int Solution_3(std::vector<int> &A)
{
uint64_t sumAll = (A.size() + 1) * (A.size() + 2) / 2;
uint64_t sumA = std::accumulate(A.begin(), A.end(), 0);
return sumAll - sumA;
}
Hope Someone explains why this happens.
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.