How to do memoisation in following c++ code - c++

Actually i am trying to solve SPOJ Problem:
[SPOJ] http://www.spoj.com/problems/SQRBR/ . I came up with recurence to solve it but i am not getting how to do memoisation. Any suggestion on how to memoisation for given problem will be helpful. my code is giving correct answer , but it is giving TLE in spoj Here my code :
#include <iostream>
#include <cstdio>
using namespace std;
void balancedParen(int n, int open, int position, int close, char str[], string s, long long int &counter) {
if(close == n) {
str[pos] = '\0';
printf("%s\n", str);
counter++;
return;
}
if(s[position] == '(' ) {
if(open <= n-1) {
str[position] = '(';
balancedParen(n, open+1, position+1, close, str, s, counter);
}
} else {
if(open < n) {
str[position] = '(';
balancedParen(n, open+1, position+1, close, str, s, counter);
}
if(open > close) {
str[position] = ')';
balancedParen(n, open, position+1, close+1, str, s, counter);
}
}
return ;
}
int main() {
int a[100], n, k, i;
long long counter = 0;
int testCases;
scanf("%d", &testCases);
while(testCases--) {
scanf("%d", &n);
scanf("%d", &k);
char str[100];
string s = "..........................................................................";
for(i = 0; i < k; i++) {
scanf("%d", &a[i]);
s[a[i]-1] = '(';
}
balancedParen(n, 0, 0, 0, str, s, counter);
printf("%lld\n", counter);
counter = 0;
}
return 0;
}

I can think of one relatively simple and possibly significant optimization.
First, instead of making the "counter" a reference, make it the return value of the function. Hear me out for a bit here.
Now say the positions you're given are "1, 7, 15". Instead of recursively going "1, 2, 3, 4, 5, 6, 7", you can be a little tricky and go to 7 in one step.
You just need to count the number of permutations which can be used to go between 1 and 7, for every possible number of opening parens (in this case, 3, 4, 5 and 6)
For example, how many ways exist to have 3 opening parens between 1 and 7?
[[[]]]
[[][]]
[][][]
[[]][]
[][[]]
5 permutations (unless I missed one). So you can add 5*balancedParen(n, open+3, position+6, close+3, str, s, counter) to your result. And do a similar thing for 4, 5, and 6 opening parens.
Of course you'd need to write another function (recursive approach seems simplest) to find that number "5". But the advantage is that the total number of function calls is now (calls to get from 1 to 7) + (calls to get from 7 to 15), rather than (calls to get from 1 to 7) * (calls to get from 7 to 15).
Here is some code which should work using the algorithm I described:
int countPermutations(int unclosed, int length, int toOpen)
{
if (toOpen > length) // impossible to open this many, not enough length
return 0;
int toClose = length-toOpen;
if (toClose - toOpen > unclosed)
return 0; // No possibilities; not enough open parens to fill the length
if (toOpen == 0 || toOpen == length)
return 1; // Only one possibility now
int ret = 0;
if (toOpen > 0) // Count permutations if we opened a paren here
ret += countPermutations(unclosed+1, length-1, toOpen-1);
if (unclosed > 0) // Count permutations if we closed a paren here
ret += countPermutations(unclosed-1, length-1, toOpen);
return ret;
}
int countNLengthSolutions(int n, int unclosed, int position, int *positions, int remainingPositions)
{
if (n % 2 != 0)
return 0; // must be a length divisible by 2
if (position > n)
return 0;
if (n-position < unclosed)
return 0; // too many open parens, no way to complete within length
if (remainingPositions == 0)
{
// Too many open parens to close by the time we get to length N?
if ((n - position) < unclosed)
return 0;
else // Say we have 4 open and a length of 10 to fill; we want (10-4)/2 = 3 more open parens.
return countPermutations(unclosed, n-position, (n-position - unclosed)/2);
}
else
{
int ret = 0;
int toFill = *positions - position - 1;
for (int openParens = 0; openParens <= toFill; openParens++)
{
int permutations = countPermutations(unclosed, toFill, openParens);
if (permutations > 0)
ret += permutations*countNLengthSolutions(n, unclosed+(2*openParens-toFill)+1, position+toFill+1, positions+1, remainingPositions-1);
}
return ret;
}
}
I may have a bug somewhere, I didn't really spend the time to check, but I verified it works for all the sample input.

Related

Will memoization work for all DP problems? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
I was solving this famous DP problem (ROD cutting) Problem Link
I understood the approach given in the editorials, but I came up with my own recursive solution..
that actually worked but the time limit is exceeding, so when I tried memoizing the same, I'm getting different answers
Code:
#include <bits/stdc++.h>
using namespace std;
// Approach 1:
// Recursion
int helper1(vector<int> &price, int size, int index, int profit)
{
if (size == 0)
return profit;
if (size < 0 or index == price.size())
return INT_MIN;
return max(helper1(price, size - (index + 1), index, profit + price[index]),
helper1(price, size, index + 1, profit));
}
int cutRod1(vector<int> &price)
{
int n = price.size();
return helper1(price, n, 0, 0);
}
// Approach 2:
// Recursion + Memoization
int helper2(vector<int> &price, int size, int index, int profit, vector<vector<int>> &dp)
{
if (size == 0)
return profit;
if (size < 0 or index == price.size())
return INT_MIN;
if (dp[index][size] != -1)
return dp[index][size];
return dp[index][size] = max(helper2(price, size - (index + 1), index, profit + price[index], dp),
helper2(price, size, index + 1, profit, dp));
}
int cutRod2(vector<int> &price)
{
int n = price.size();
vector<vector<int>> dp(n + 1, vector<int>(n + 1, -1));
return helper2(price, n, 0, 0, dp);
}
void solve()
{
vector<int> price{3, 9, 13, 12, 8, 12, 8, 8, 3, 10, 13};
cout << cutRod1(price) << endl;
cout << cutRod2(price) << endl;
}
int main()
{
ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t{1};
// cin >> t;
while (t--)
solve();
return 0;
}
Recursive solution's result: 49 (Correct)
Memoized solutions's result: 48 (Wrong)
Intuition behind my approach:
It is an unbounded Knapsack problem, we have two choices:
Take a cut at index's position and stay there (for making furthur cuts at the same position)
Not take a cut at index's position, move to next index (index+1)
can anyone help me finding out the reason why memoized version doesn't work
You need to include profit in the memoization lookup. At some point, you're calling helper with the same (size, index) pair but the passed in profit value is different.
This is a solution with memoization. The source contains a complete explanation. Source
#include <iostream>
#include<algorithm> // To use the built in max function
using namespace std;
int main() {
// Suppose that we have a rod of length 5, and an array containing the
// length(1,2,3 and 4 ) and price(2,5,7 and 8 ) of the pieces.
int price[] = {2,5,7,8};
int n = 5;
// Declaring a 2D array, T
int T[n-1][n+1];
// Initializing the array to all zeros
for(int i=0; i < n-1; i++)
{
for(int j=0; j < n+1; j++ )
{
T[i][j] = 0;
}
}
for(int i=0; i < n-1; i++)
{
for(int j=0; j < n+1; j++ )
{
// First column => 0 length of rod => 0 profit
if(j == 0) {
continue;
}
// First row => T[i-1][j] doesn't exist so just pick the second value
else if(i == 0) {
T[i][j] = price[i] + T[i][j-i-1];
}
// where j <= i => T[i][j-i-1] doesn't exist so just pick the first value
else if(j-i-1 < 0) {
T[i][j] = T[i-1][j];
}
// using the whole expression
else {
T[i][j] = std::max(T[i-1][j], (price[i] + T[i][j-i-1]));
}
}
}
// Answer in the extreme bottom right cell
cout << "Maximum profit is " << T[n-2][n] << endl;
return 0;
}

Alphabetically partitioning indexes of substrings in C++ issues

for a while now I've been trying to get this code to work to partition (as if preparing for quicksort) the indexes of substring suffixes, and while it's close I'm not getting what I'm expecting. I was wondering if a fresh set of eyes may help.
int partition(const string &S, vector<int> &indices, int low, int high, int pivotIndex)
{
int i = low;
int j = high - 1;
swap(indices[high], indices[pivotIndex]);
while (i <= j)
{
while (i < high && !lessThan(S, S[i], S[indices[high]]))
i++;
while (j >= low && lessThan(S, S[j], S[indices[high]]))
j--;
if (i < j)
{
int temp = indices[i];
indices[i] = indices[j];
indices[j] = temp;
i++;
j--;
}
}
swap(indices[high], indices[i]);
return i;
}
Indices is simply a vector of 0, 1, 2, ..., n of same size as string S.
And here's the program I wrote for lessThan just so you know what I'm working with:
bool lessThan(const string &S, int first, int second)
{
int counter = (int)S.length() - ((first <= second) ? second : first);
for (int i = 0; i <= counter; ++i)
{
if (S[first + i] != S[second + i])
{
if (S[first + i] < S[second + i])
{
return true;
}
else
{
return false;
}
}
}
if (first < second)
{
return false;
}
else
{
return true;
}
}
lessThan seems to work just fine when I test it separately, so I don't think it's the issue, but maybe it is.
Whenever I test, with say the string "abracadabra", and setting the pivotIndex to 4, I expect to get "0 1 8 3 10 5 7 4 2 9 6" as my output but I instead get "0 1 8 3 7 5 4 10 2 9 6". Close, but not quite. Can anyone spot my mistake?
(P.S. I know I could probably use substr() or some other solution to do lessThan easier, but I'm trying to do it without allocating extra memory, my focus is on the partition function)
edit: I figured it out. Complete error on my side. Check below for answer
I was an idiot, input to lessThan was supposed to be given two ints. I instead gave it two chars from S. Also swapped which lessThan call the ! was on. I think I was just up too late programming and blame this all on sleep deprivation.
Fixed Code:
int partition(const string &S, vector<int> &indices, int low, int high, int pivotIndex)
{
int i = low;
int j = high - 1;
swap(indices[high], indices[pivotIndex]);
while (i <= j)
{ //This right here
while (i < high && lessThan(S, indices[i], indices[high]))
i++;
while (j >= low && !lessThan(S, indices[j], indices[high]))
j--;
if (i < j)
{
swap(indices[i], indices[j]);
i++;
j--;
}
}
swap(indices[high], indices[i]);
return i;
}

Is there a way to ignore elements in an array without changing the array in a recursive function?

I'm trying to write a recursive function that checks if two arrays have the same elements even if they aren't sorted, but I I can't change the arrays and I can't copy them or use a third/fourth arrays and it has to be recursive, lastly, I can't change the signature of the function.
So now I have to get rid of overwrite(A2, len, i); because that's destroying A2, but I don't see any way to do it and still have a working function... can I have a hint on how to do it? Maybe there's a way to save the elements of A2 by swapping them and then by the end of the recursion to restore them?
In short the algorithm below does a linear search of the last element of A1 in A2, if it's found, overwrite it and continue, this is done so the algorithm won't pick the same element twice, reaching the stopping condition means all the elements are there thus it will return true, otherwise will return false.
bool foo(int A1[], int A2[], int len){//both arrays are size len
int i;
bool found = false;
if (len == 0)return true;//stopping condition for recursion
else{
for (i = 0; i < len && !found; i++)//linear search
if (A1[len - 1] == A2[i]){
overwrite(A2, len, i);//this function shifts back the whole array
found = true;
}
if (found == false) return false;
else foo(A1, A2, len - 1);
}
}
Sample i/o:
A1: 3 2 1
A2: 1 2 3
True
A1: 3 2 3
A2: 1 2 3
False
A solution could be:
find what is the maximum value M in in A1 and how many times it appears
check if it's the same for A2, including the count
find what is the maximum value M1 among all values smaller than M and how many times is present in A1
check if it's the same for A2, including the count
find what is the maximum value M2 among all values smaller than M1 and how many times is present in A1
check if it's the same for A2, including the count
repeat this way until the counter for A1 and A2 is zero or is different
in code:
bool checkSame(int *A1, int *A2, int len) {
struct Same {
static bool check(int *A1, int *A2, int len, int limit) {
int index1=-1, count1=0;
for (int i=0; i<len; i++) {
if (A1[i] <= limit) {
if (index1==-1 || A1[i] > A1[index1]) {
index1 = i;
count1 = 1;
} else if (A1[i] == A1[index1]) {
count1++;
}
}
}
int index2=-1, count2=0;
for (int i=0; i<len; i++) {
if (A2[i] <= limit) {
if (index2==-1 || A2[i] > A2[index2]) {
index2 = i;
count2 = 1;
} else if (A2[i] == A2[index2]) {
count2++;
}
}
}
if (index1 == -1 && index2 == -1) return true;
if (count1 != count2 || count1 == 0 ||
A1[index1] != A2[index2]) return false;
return check(A1, A2, len, A1[index1]-1);
}
};
return Same::check(A1, A2, len, INT_MAX);
}
This algorithm is O(n^2) in time (worst case: arrays are identical and all values unique) and requires constant space if the compiler supports tail call optimization.
The following is a chart for the time needed in ms from 0 to 3000 elements on my PC.
Note that however all this is not a decent solution for the problem but just an exercise in futility. A real solution of course would need more context as there are different criteria for optimality, but I'd probably go for a closed hash table... adding elements while processing A1 and removing elements processing A2 (the removal will fail at some point if and only if the arrays are different):
bool checkSame2(int *A1, int *A2, int len) {
std::vector<int> ht(len, -1), next(len, -1);
for (int i=0; i<len; i++) {
int k = (unsigned)A1[i]*69069 % len;
next[i] = ht[k]; ht[k] = i;
}
for (int i=0; i<len; i++) {
int k = (unsigned)A2[i]*69069 % len;
int prev=-1,p=ht[k];
while (p!=-1 && A1[p] != A2[i]) {
prev = p; p = next[p];
}
if (p == -1) return false;
if (prev == -1) ht[k] = next[p]; else next[prev] = next[p];
}
return true;
}
The execution time for this solution is the purple line touching the N axis in the previous chart (hard to tell with this scale but it's linear + noise, as expected).
Just out of curiosity I also tried what would be the solution if "optimal" means just getting something working that is not hideous:
bool checkSame3(int *A1, int *A2, int len) {
std::map<int, int> counts;
for (int i=0; i<len; i++) counts[A1[i]]++;
for (int i=0; i<len; i++) {
if (--counts[A2[i]] < 0) return false;
}
return true;
}
and this is, unsurprisingly, about 30-40 times slower than the hand-coded hash table version on my PC (but of course still much faster than the recursive version).
Here is a solution that works given all your requirements. It rearranges the arrays, and then un-rearranges them. It uses recursion, uses no additional arrays, and does not change the function signature.
bool foo(int A1[], int A2[], int len){
int i;
if (len == 0){
return true;
} else {
for (i = len - 1; i >= 0; i--){
if (A1[len - 1] == A2[i]){
A2[i] = A2[len - 1];
A2[len - 1] = A1[len - 1];
bool result = foo(A1, A2, len - 1);
A2[len - 1] = A2[i];
A2[i] = A1[len - 1];
return result;
}
}
return false;
}
}
If you are allowed to temporarily change the arrays, provided that you restore them before the last recursive call has returned, you can swap the matching element in A2 with the element at index len - 1 before the recursive call, and swap them back afterwards. Since the recursive call will only look at the index range 0 through len - 2, the matching element will not be considered.

DP - Counting coin change

The problem requires to count number of coin changes for a particular cost.
For example, if I have coin values of 50, 20, 10, 5, 1, I can form costs of:
5 => (5), (11111), which are 2 ways.
10 => (10), (5, 5), (5, 11111), (11111, 11111), which are 4 ways.
Here is my function. It is returning wrong results begging from cost of 10 (returns 9 ways while the actual number of ways is only 4)
int dp[10000];
int coins[] = { 50, 20, 10, 5, 1 };
int rec(int n)
{
if (n == 0) return 1;
if (dp[n] != -1) return dp[n];
int cnt = 0;
for (int i = 0; i < 5; i++)
if (coins[i] <= n) cnt += rec(n - coins[i]);
return dp[n] = cnt;
}
How can I fix this function to give the correct number of ways? Is this algorithm correct even? see the complete code and its output here
NOTE: my problem is not with dp array initialization. I am using memset to initialize it to -1 each time before calling rec.
(5, 1, 1, 1, 1, 1) and (1, 1, 1, 5, 1, 1) is different way in you algorithm, you should keep it decreasing.
int dp[10000][5]; // dp[20][2] means, if the biggest coin is coins[2],
// how much ways for 20 ?
int coins[] = { 1, 5, 10, 20, 50 }; // here
int rec(int n, int m)
{
int cnt = 0;
int i;
if (n == 0) return 1;
//if (m == 0) return 1;
if (dp[n][m] != -1) return dp[n][m];
for (i = 0; i <= m; i++)
if (coins[i] <= n) cnt += rec(n - coins[i], i);
return dp[n][m] = cnt;
}
int main()
{
memset(dp, -1, sizeof(dp));
printf("%d\n", rec(10, 4));
}
The result is wrong since you never make sure that your algorithm starts with the 5 coin. (5,11111) is just as valid in your code as (1, 5, 1111), but this is the same result. Your result should be wrong from 6 and higher, not 10 and higher.
To fix this you can do like a cutoff in your function rec():
int rec(int n, int cutoff)
{
if (n == 0) return 1;
if (dp[n] != -1) return dp[n];
int cnt = 0;
for (int i = cutoff; i < 5; i++)
if (coins[i] <= n) cnt += rec(n - coins[i], i);
return dp[n] = cnt;
}
Should do it.
Edit: you will have to take care of your dp[] array, since it does not care about this cutoff, but this in general is the fault you are running into. You could comment that line, and check if this works.
One remark: Your initialization
memset(dp, -1, sizeof dp);
is not really safe. memset initializes every byte of a memory space (see http://www.cplusplus.com/reference/clibrary/cstring/memset/.). For this particular case you are lucky and the representation of int(-1) is (probably) the same of four times unsigned char(-1).
I would suggest using std::fill ( http://www.cplusplus.com/reference/algorithm/fill/ ).

Function to check if any combination of numbers in a vector will add up to an int?

To start with, I'm looking for something simple and easy to understand rather than the most efficient.
I am trying to create a function that will take in a vector, and an int. The function should return true if any numbers in the vector can add up to the int.
The vector will start with the numbers 1,2,3,4,5,6,7,8,9,10 in it, and throughout the program numbers will be removed. There will be no duplicates of numbers.
The int can be any number from 2 through 12.
Some examples:
vector = { 2,3,4,5 } int = 7; function returns true because 3 + 4 = 7.
vector = { 1,5,8 } int = 7; function returns false because none of these numbers can add to 7.
vector = { 3,6 } int = 3; function returns true because 3 = 3.
vector = { 5 } int = 2; function returns false because five cannot add to two.
This is the very last function I need to finish a game I am working on. I feel like I'm missing an easy solution, but I'm not sure. Can anyone show me how to do this, or point me in the right direction of how to solve this problem? Thank you in advance.
Given the additional information in the comments, the following function should do (I'm assuming the same number cannot be used twice in the sum):
typedef std::vector<int>::iterator iter;
bool contains_sum(iter begin, iter end, int sum)
{
while (begin != end)
{
--end;
if (*end > sum)
continue;
if (contains_sum(begin, end, sum - *end))
return true;
}
return sum == 0;
}
Isn't this a case of the knapsack problem?
See also: subset sum
What you need to do is to find all possible combinations and then check if any of those have the right sum. A double-recursive function can make the check.
bool canFormSum(vector<int>::iterator rest, vector<int>::iterator end,
int sumSoFar, int targetSum)
{
if(rest == end) return false;
if(sumSoFar + *rest == targetSum) return true;
if(canFormSum(rest + 1, end, sumSoFar, targetSum)) return true;
if(sumSoFar + *rest > targetSum) return false;
return canFormSum(rest + 1, end, sumSoFar + *rest, targetSum);
}
It's a nice example of recursive calculation - but for anything but small vectors it has horrible performance.
For the general cases (size of vector > 10),
Let f({a, b, c, d, ...}, e) be the result of whether any number in the set {a, b, c, d, ...} be equal to e.
Observe that, if e = x + y + z + ..., then either (1) a is in the set of {x, y, z, ...}, or (2) a is not in the set of {x, y, z, ...}. That means, we have the recursive definition:
f({a, etc...}, e) = f({etc...}, e-a) || f({etc...}, e)
And obviously, if the sum is 0, the relation is always true by not including any element from the set:
f({...}, 0) = true
and if the set is empty and the sum is nonzero, the relation is always false:
f({}, e) = false (if e != 0)
These are the base cases of the recursion.
Edit: See also the Subset sum problem for further discussion.
Just to compare my (C) solution with celtschk (c++) solution. (Basically compared the approach, not the languages)
#include <iostream>
#include <vector>
using namespace std;
int counter = 0;
typedef std::vector<int>::iterator iter;
bool contains_sum(iter begin, iter end, int sum)
{
counter ++;
while (begin != end)
{
--end;
if (contains_sum(begin, end, sum - *end))
return true;
}
return sum == 0;
}
int main () {
vector<int> data;
for (int i = 1; i <= 30; i ++) {
data.push_back(i);
}
int target = 77;
if (contains_sum (data.begin(), data.end(), target)) {
cout << "possible\n" << counter;
} else {
cout << "not possible\n << counter";
}
}
output
possible
268304387
Means nearly 270 million calls of recursive method
Now my approach
#include<stdio.h>
#include<stdlib.h>
int counter;
int check (int* in, int sum) {
counter ++;
while (1) {
int act = *in++;
if (act == 0) return 0;
int rest = sum - act;
if (rest == 0) return 1; // found;
if (rest > 0) {
if (1 == check (in, rest)) return 1; // found
}
}
return -1;
}
void pr (int* in, int sum) {
counter = 0;
int res = check (in, sum);
while (*in) {
printf ("%d ", *in ++);
}
if (res == 0) {
printf(" != %d %d\n", sum, counter);
} else {
printf(" == %d %d\n", sum, counter);
}
}
int main () {
int p0[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
0};
pr (p0, 77);
int p1[] = {2,3,4,5, 0};
pr (p1, 7);
int p2[] = {1,5,8, 0};
pr (p2, 7);
int p3[] = {3,6, 0};
pr (p3, 3);
int p4[] = {5, 0};
pr (p4, 2);
int p5[] = {1, 100, 6, 0};
pr (p5, 7);
return 0;
}
and this is the output
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29 30 == 77 22
2 3 4 5 == 7 4
1 5 8 != 7 4
3 6 == 3 1
5 != 2 1
1 100 6 == 7 2
Ups, just 22 iterations!
Anyone may decide which approach to consider more "elegant"
You have to try all possible combination until you found a solution. SUch problems would be good for the language "prolog". So we have to simulate backtracking.
this code is in the end c.
#include<stdio.h>
int check (int* in, int sum) {
while (1) {
int act = *in++;
if (act == 0) return 0;
int rest = sum - act;
if (rest > 0) { // test in the order of expected likelyhoods
if (1 == check (in, rest)) return 1; // found
}
// if (rest < 0) return 0; // minor optimization, valid if list is ordered ascendant
if (rest == 0) return 1; // found;
}
//return -1; // only necessary on poor compilers
}
void pr (int* in, int sum) {
int res = check (in, sum);
while (*in) {
printf ("%d ", *in ++);
}
if (res == 0) {
printf(" != %d\n", sum);
} else {
printf(" == %d\n", sum);
}
}
int main () {
int p1[] = {2,3,4,5, 0};
pr (p1, 7);
int p2[] = {1,5,8, 0};
pr (p2, 7);
int p3[] = {3,6, 0};
pr (p3, 3);
int p4[] = {5, 0};
pr (p4, 2);
int p5[] = {1, 100, 6, 0};
pr (p5, 7);
return 0;
}
proven to run
2 3 4 5 == 7
1 5 8 != 7
3 6 == 3
5 != 2
1 100 6 == 7