I wrote a recursive solution for the longest increasing subsequence and it worked perfectly fine. But when I applied dp on the same code it gives different answers.
Problem Link: https://practice.geeksforgeeks.org/problems/longest-increasing-subsequence-1587115620/1
Recursive code:
int LISrecursive(int arr[], int n, int currIndex, int maxVal) {
if (currIndex == n) {
return 0;
}
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISrecursive(arr, n, currIndex + 1, arr[currIndex]);
}
notIncluded = LISrecursive(arr, n, currIndex + 1, maxVal);
return max(notIncluded, included);
}
DP Code:
int LISdp(int arr[], int n, int currIndex, int maxVal, vector<int> &dp) {
if (currIndex == n) {
return 0;
}
if (dp[currIndex] != -1) return dp[currIndex];
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], dp);
}
notIncluded = LISdp(arr, n, currIndex + 1, maxVal, dp);
return dp[currIndex] = max(notIncluded, included);
}
int32_t main() {
int n;
cin >> n;
int arr[n];
vector<int> dp(n, -1);
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
cout << LISrecursive(arr,n,0,-1);
cout << LISdp(arr, n, 0 , -1, dp);
return 0;
}
I cannot figure out what I did wrong?
For this test case
6 (n)
6 3 7 4 6 9 (arr[])
Recursive code gives 4 answer(correct)
But DP code gives 3 answer(incorrect)
When I think of dynamic programming, I usually break it down into two steps:
Solve the recursion with "including the current element before recursing
again" compared to "not including the current element before recursing again". This is exactly what you did with your recursive solution.
Take the recursive solution from step 1 and add a cache of previous computed results to avoid repetitive recursion. The cache, can be conceptualized as a multidimension matrix that maps all the non-const variable parameters passed to the recursive function to the final result.
In your case, each recursive step has two variables, currIndex, and maxVal. a and n are actually constants throughout the entire recursion. The number of non-const parameters of the recursive step is the number of dimensions in your cache. So you need a two dimensional table. We could use a big 2-d int array, but that would take a lot of memory. We can achieve the same efficiency with a nested pair of hash tables.
Your primary mistake is that your cache is only 1 dimension - caching the result compared to currIndex irrespective of the value of maxVal. The other mistake is using a vector instead of a hash table. The vector technique you have works, but doesn't scale. And when we add a second dimension, the scale in terms of memory use are even worse.
So let's defined a cache type as an unordered_map (hash table) that maps currIndex to another hash table that maps maxVal to the result of the recursion. You could also use tuples, but the geeksforgeeks coding site doesn't seem to like that. No bother, we can just define this:
typedef std::unordered_map<int, std::unordered_map<int, int>> CACHE;
Then your DP solution is effectively just inserting a lookup into the CACHE at the top of the recursive function and an insertion into the CACHE at the bottom of the function.
int LISdp(int arr[], int n, int currIndex, int maxVal, CACHE& cache) {
if (currIndex == n) {
return 0;
}
// check our cache if we've already solved for currIndex and maxVal together
auto itor1 = cache.find(currIndex);
if (itor1 != cache.end())
{
// itor1->second is a reference to cache[currIndex]
auto itor2 = itor1->second.find(maxVal);
if (itor2 != itor1->second.end())
{
// itor2->second is a reference to cache[n][maxVal];
return itor2->second;
}
}
int included = 0, notIncluded = 0;
if (arr[currIndex] > maxVal) {
included = 1 + LISdp(arr, n, currIndex + 1, arr[currIndex], cache);
}
notIncluded = LISdp(arr, n, currIndex + 1, maxVal, cache);
// cache the final result into the 2-d map before returning
int finalresult = std::max(notIncluded, included);
cache[currIndex][maxVal] = finalresult; // cache the result
return finalresult;
}
Then the initial invocation with the input set to solve for is effectively passing INT_MIN as the intial maxVal and an empty cache:
int N = 16
int A[N]={0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15};
CACHE cache;
int result = LISdp(A, N, 0, INT_MIN, cache);
A minor optimization is to make a, n, and cache a member variable of the C++ class encapsulating your solution so that they don't have to be pushed onto the stack for each step of the recursion. The cache is getting passed by reference, so it's not that big of a deal.
You have 2 problems in your code:
Mistake 1
First in C++, the size of an array must be a compile-time constant.So, take for example the following code snippets:
int n = 10;
int arr[n]; //INCORRECT because n is not a constant expression
The correct way to write the above would be:
const int n = 10;
int arr[n]; //CORRECT
Similarly, the following(which you did in your code example) is incorrect:
int n;
cin >> n;
int arr[n];// INCORRECT because n is not a constant expression
Mistake 2
Second in your function LISdp, it seems to me that there is no need of the statement
if (dp[currIndex] != -1) return dp[currIndex];//no need for this statement
You should just remove this(the above) statement and the program produces expected output 4 as can be seen here. Basically you have not thought this(LISdp's working) through. You can use the debugger to see where you're going wrong.
There might be other problems in your code but so far i am able to spot these 2.
Related
I have an assignment for a c++ programming class to write a recursive function without the use of static variables, with the following prototype:
int findmin(const int a[], int n);
My solution works (for very small arrays), however I think ~2^n complexity is excessive and could be improved.
Are there any improvements that could be made within the specified criteria that would make this more efficient?
int findmin(const int a[], int n)
{
if(n == 0)
return a[0];
else
{
if(a[n-1] < findmin(a,(n-1)))
return a[n-1];
else
return findmin(a,(n-1));
}
}
It's a little silly to worry about efficiency, given that there is an obvious, non-recursive way to do it in O(n), one pass. There is even an STL algorithm std::min_element. But then, it's a silly assignment. FIrst be sure your solution is correct. When n==0, will a[0] be valid? Generally, such an n indicates the length of the array, not the lowest index.
To go from O[n^2] to O[n], be sure to compare each element only once. That implies not starting at the beginning of the array on every pass.
#include <algorithm>
#include <cassert>
int findmin(const int a[], int n)
{
assert(n>0);
if(n == 1) // See heyah.
return a[0];
else
{
return std::min(a[0], findmin(a + 1, n - 1));
}
}
In for-real C++ code, if for some reason we were saddled with the old fashion function signature, we would do something like this:
int findmin(const int a[], int n) {
if(n<=0) { throw std::length_error("findmin called on empty array");}
return *std::min_element(a, a+n);
}
You could do conditional operator ?: to get rid of bunch if else statements, to make function cleaner. And instead of calling findmin() twice you could assign return value to variable inside of the statement, this is main advantage of this code vs. original one.
int findmin(const int a[], int n) {
if (n == 0) // base case
return a[0];
return a[n] < (int min = findmin(a, n - 1)) ? a[n] : min;
}
This (a[n] < (int min = findmin(a, n - 1)) ? a[n] : min;) could be done using if statement as well:
if (a[n] < (int min = findmin (a, n - 1))
return a[n];
else
return min;
EDIT:
Per many reputable sources, this is O(n) time. O (n^2) would be if we are comparing each element with all the other elements.
I'm having a hard time thinking of a way to make a solution for a dynamic programming problem. Basically, I have to get from the top left to bottom right element in a NxN array, being able to move only down or right, but I should move to the bigger element and sum it in a variable (get highest score by moving only right and down). F.e., if I have this matrix:
0 1 1
0 4 2
1 1 1
It should move 0-> 1 -> 4 -> 2 -> 1 and print out 8.
I've read about dynamic optimizing for a long time now and still can't get to solve this. Would appreciate if anybody could help me.
Thanks in advance!
Edit: Thanks #sestus ! I've managed to solve the problem, however the solution is slow and I have to optimize it to perform faster. Here's my solution:
#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 100;
int arr[MAX][MAX];
int move(int row,int col, int n)
{
if(row >= n || col >= n)
{
return 0;
}
return max(arr[row][col] + move(row + 1, col, n),
arr[row][col] + move(row, col + 1, n));
}
int main()
{
int examples, result;
cin>>examples;
int n;
int results[examples];
for(int k =1; k <= examples; k++)
{
cin >> n;
int s = 0;
int i = 0, j = 0;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
cin>> arr[i][j];
}
}
i = 0, j = 0;
s+=move(i,j, n);
results[k] = s;
}
for(int k = 1; k <= examples; k++)
{
cout<<results[k]<<endl;
}
return 0;
}
(The programs actually has to take all the examples and output the answers for all of them at the end). Mind helping me with the optimizing?
I m not going to paste ready-to-go code here, I ll just give you a high level description of the solution.
So what are your possible choices when deciding where to move? You can move either down or right, adding the value of your current block. Your aim is to maximize the sum of the blocks that you have visited till you make it to the bottom-right block. That gives us:
move(row, column):
//handle the cases when you move out of bounds of the array here
return(max{array[row,column] + move(row + 1, column),
array[row,column] + move(row, column + 1)})
For the above to be a complete dynamic programming solution, you ll need to add some memoization e.g get the values of the problems that you 've already solved without having to compute them again. Check this on stackoverflow for more details : Dynamic programming and memoization: bottom-up vs top-down approaches
So for example, take this board:
Notice the two different routes. We 've arrived to the block[1][2] (the one with value 3 that the red and blue lines end) via two different routes. According to the blue route, we moved down-right-right, while we moved right-right-down via the read. The pseudocode I pasted dictates that we are going to take the blue route first, cause we encounter the recursive call move(row + 1, column) prior to the recursive call move(row, column + 1).
So, when we reach the block[1][2] from the red route, we don't actually need to compute this solution again. We 've already done this, back when we were there via the read route! If we kept this solution in an array (or a map / hash table), we 'd be able to just pick the solution without having to compute it again. That's memoization!
Based on the above, you can utilize a map since you 're using c++:
std::map<std::pair<int, int>, int> cache;
And before doing the recursive call, you ll want to check if the pair exist in the map. If it doesn't you add it in the map. So move becomes:
int move(int row,int col, int n)
{
if(row >= n || col >= n)
{
return 0;
}
pair<int, int> rowplus_column = make_pair(row + 1,col);
pair<int, int> row_columnplus = make_pair(row, col + 1);
int solution_right = 0;
int solution_down = 0;
map<char, int>::iterator it;
it = cache.find(rowplus_column);
if (it == cache.end()) {
solution_down = move(row + 1, col);
cache.insert(rowplus_column, solution_down);
}
else {
solution_down = it->second;
}
it = cache.find(row_columnplus);
if (it == cache.end()) {
solution_right = move(row, col + 1);
cache.insert(row_columnplus, solution_right);
}
else {
solution_right = it->second;
}
return max(arr[row][col] + solution_down,
arr[row][col] + solution_right);
}
I am a little rusty in C++, but hopefully you got the idea:
Before actually computing the solution, check the map for the pair. If that pair exists, you 've already solved that part of the problem, so get your solution from the map and avoid the recursive call.
#include <iostream>
using namespace std;
void moveToKthSmallest(int a[], int size, int k);
int main() {
int theArray[17] = {42, 5, 412, 56, 14, 98, 488, 4882, 24, 4, 9, 67, 9424, 2, 1, 3, 5};
int num;
cout << "Move to the kth smallest number in the array (size of 17): " << endl;
cin >> num;
moveToKthSmallest(theArray, 17, num);
return 0;
}
void moveToKthSmallest(int a[], int size, int k) {
int pivot = size / 2;
int pivotValue = a[pivot];
int index1 = 0, index2 = 0;
int s1[size], s2[size]; //not sure about this
for (int i = 0; i < size; i++) {
if (a[i] < pivotValue) {
s1[index1] = a[i];
index1++;
}
else {
s2[index2] = a[i];
index2++;
}
}
int s1Size = index1; //problem?
int s2Size = index2; //problem?
if (s1Size == k - 1) {
cout << pivotValue;
}
else if (s1Size > (k - 1)) {
moveToKthSmallest(s1, s1Size, k);
}
else if (s1Size < (k - 1)) {
moveToKthSmallest(s2, s2Size, k - (s1Size - 1));
}
}
I ask the user to input a number (greater than or equal to 0 or less than or equal to 17) and the program should output the kth smallest number in the already generated array.
The above is my attempt but it crashes whenever I run it. I think it has something to do with the declaration of the arrays s1 and s2 in the function and possibly the size values generated from index1 and index2. I've tried using vectors and dynamic arrays but I keep getting error messages in my compiler such as "error: cannot convert 'std::vector**' to 'int*' for argument '1' to 'void moveToKthSmallest" ... to be quite honest, I'm not quite sure what that means.
Am I missing something obvious here?
The first point of order is that this is tagged C++ and not C. Therefore, instead of an int a[] and an int size, you should be using a straightforward std::vector<int>.
Now, with that out of the way, in your example, you're iterating over the entire array each time you recurse.
The second point of order, I don't understand why recursion is even needed here. The answer can be found with a single iteration over your array.
The most simplest way of doing that is to use a second array, let's say std::vector<int> smallest, then iterate over your std::vector<int> a just once.
When iterating over each element:
if smallest.size() is already k, and the current value is larger than the last value in smallest, then proceed to the next value in a, otherwise remove the last value in smallest.
at this point, smallest.size() is always less than k, therefore add the current value in a to the smallest in a manner that keeps the smallest array in sorted order.
At the end of the iteration, the last value in the smallest vector is the kth smallest value in your original vector.
If you would like to know the position of the kth smallest value, this is just a minor modification to the above algorithm, by tracking the position in the smallest vector, rather than the value.
This seems to be a much simpler, straightforward solution, then a complicated recursion-based approach with a confusing pivoting operation. Also, your pivoting example requires the original array to be modified, while my approach does not, and can be used with a const std::vector<int>, even.
P.S. It's not really necessary to keep smallest in sorted order. With a slight modification to the algorithm, smallest can remain unsorted. The homework assignment here is to adjust this algorithm to not require smallest to remain in sorted order.
So for an assignment I've been asked to create a function that will generate an array of fibonacci numbers and the user will then provide an array of random numbers. My function must then check if the array the user has entered contains any fibonacci numbers then the function will output true, otherwise it will output false. I have already been able to create the array of Fib numbers and check it against the array that the user enters however it is limited since my Fib array has a max size of 100.
bool hasFibNum (int arr[], int size){
int fibarray[100];
fibarray[0] = 0;
fibarray[1] = 1;
bool result = false;
for (int i = 2; i < 100; i++)
{
fibarray[i] = fibarray[i-1] + fibarray[i-2];
}
for (int i = 0; i < size; i++)
{
for(int j = 0; j < 100; j++){
if (fibarray[j] == arr[i])
result = true;
}
}
return result;
}
So basically how can I make it so that I don't have to use int fibarray[100] and can instead generate fib numbers up to a certain point. That point being the maximum number in the user's array.
So for example if the user enters the array {4,2,1,8,21}, I need to generate a fibarray up to the number 21 {1,1,2,3,5,8,13,21}. If the user enters the array {1,4,10} I would need to generate a fibarray with {1,1,2,3,5,8,13}
Quite new to programming so any help would be appreciated! Sorry if my code is terrible.
It is possible that I still don't understand your question, but if I do, then I would achieve what you want like this:
bool hasFibNum (int arr[], int size){
if (size == 0) return false;
int maxValue = arr[0];
for (int i = 1; i < size; i++)
{
if (arr[i] > maxValue) maxValue = arr[i];
}
int first = 0;
int second = 1;
while (second < maxValue)
{
for (int i = 0; i < size; i++)
{
if (arr[i] == first) return true;
if (arr[i] == second) return true;
}
first = first + second;
second = second + first;
}
return false;
}
Here is a function that returns a dynamic array with all of the Fibonacci numbers up to and including max (assuming max > 0)
std::vector<size_t> make_fibs( size_t max ) {
std::vector<size_t> retval = {1,1};
while( retval.back() < max ) {
retval.push_back( retval.back()+*(retval.end()-2) );
}
return retval;
}
I prepopulate it with 2 elements rather than keeping track of the last 2 separately.
Note that under some definitions, 0 and -1 are Fibonacci numbers. If you are using that, start the array off with {-1, 0, 1} (which isn't their order, it is actually -1, 1, 0, 1, but by keeping them in ascending order we can binary_search below). If you do so, change the type to an int not a size_t.
Next, a sketch of an implementation for has_fibs:
template<class T, size_t N>
bool has_fibs( T(&array)[N] ) {
// bring `begin` and `end` into view, one of the good uses of `using`:
using std::begin; using std::end;
// guaranteed array is nonempty, so
T m = *std::max_element( begin(array), end(array) ); will have a max, so * is safe.
if (m < 0) m = 0; // deal with the possibility the `array` is all negative
// use `auto` to not repeat a type, and `const` because we aren't going to alter it:
const auto fibs = make_fibs(m);
// d-d-d-ouble `std` algorithm:
return std::find_if( begin(array), end(array), [&fibs]( T v )->bool {
return std::binary_search( begin(fibs), end(fibs), v );
}) != end(array);
}
here I create a template function that takes your (fixed sized) array as a reference. This has the advantage that ranged-based loops will work on it.
Next, I use a std algorithm max_element to find the max element.
Finally, I use two std algorithms, find_if and binary_search, plus a lambda to glue them together, to find any intersections between the two containers.
I'm liberally using C++11 features and lots of abstraction here. If you don't understand a function, I encourage you to rewrite the parts you don't understand rather than copying blindly.
This code has runtime O(n lg lg n) which is probably overkill. (fibs grow exponentially. Building them takes lg n time, searching them takes lg lg n time, and we search then n times).
The problem is to count the number of of values less than the value after index. Here is the code, but I can't understand how binary indexed tree has been used to do this?
#include <iostream>
#include <vector>
#include <algorithm>
#define LL long long
#define MOD 1000000007
#define MAXN 10
using namespace std;
typedef pair<int, int> ii;
int BIT[MAXN+1];
int a[MAXN+1];
vector< ii > temp;
int countSmallerRight[MAXN+1];
int read(int idx) {
int sum = 0;
while (idx > 0) {
sum += BIT[idx];
idx -= (idx & -idx);
}
return sum;
}
void update(int idx, int val) {
while (idx <= MAXN) {
BIT[idx] += val;
idx += (idx & -idx);
}
}
int main(int argc, const char * argv[])
{
int N;
scanf("%d", &N);
for (int i = 1; i <= N; i++) {
scanf("%d", &a[i]);
temp.push_back(ii(a[i], i));
}
sort(temp.begin(), temp.end());
countSmallerRight[temp[0].second] = 0;
update(1, 1);
update(temp[0].second, -1);
for (int i = 1; i < N; i++) {
countSmallerRight[temp[i].second] = read(temp[i].second);
update(1, 1);
update(temp[i].second, -1);
}
for (int i = 1; i <= N; i++) {
printf("%d,", countSmallerRight[i]);
}
putchar('\n');
return 0;
}
It would be helpful if someone could explain the working principal of the code.
to understand BIT this is one of the best links .
TC gives the full explaination of functions you used , but rest part is logic on how to use it .
For basic understanding :
ques: there are n heaps and in each heap initially there are 1 stones then we add stones from u to v…find how much stone are there in given heap.
the solution , with answer at each iteration is http://pastebin.com/9QJ589VR.
After you understand it , try to implement your question .
A better proof and motivation behind Binary Index trees can be found here.
https://cs.stackexchange.com/questions/10538/bit-what-is-the-intuition-behind-a-binary-indexed-tree-and-how-was-it-thought-a
Let's suppose, for example, that you want to store cumulative frequencies for a total of 7 different elements. You could start off by writing out seven buckets into which the numbers will be distributed
Change the representation from being an array of buckets to being a binary tree of nodes.
If you treat 0 to mean "left" and 1 to mean "right," the remaining bits on each number spell out exactly how to start at the root and then walk down to that number.
The reason that this is significant is that our lookup and update operations depend on the access path from the node back up to the root and whether we're following left or right child links. For example, during a lookup, we just care about the right links we follow. During an update, we just care about the left links we follow. This binary indexed tree does all of this super efficiently by just using the bits in the index.
If you don't care about the proof:
I googled BIT for dummies and found this
https://www.hackerearth.com/practice/data-structures/advanced-data-structures/fenwick-binary-indexed-trees/tutorial/
Property of a perfectly binary tree:
Given node n, the next node on the access path back up to the root in which we go right is given by taking the binary representation of n and removing the last 1.
Why isolate the last bit?
When we isolate the last bit, the index x only goes to indexes ((+/-)x&(-x)) whose update is neccesary or whose value is required during a lookup.
while query we go down the array and while update we go up the array.
For example query(6) is going to add sum at BIT[6] but also add sum at BIT[4] and BIT[0] because 6(110) - 2 = 4(100) - 4 = 0.
6(110)'s last bit is 2(10). Hence we do 6-2.
4(100)'s last bit is 4(100). Hence we do 4-4.
we stop when x==0.
Use the same logic for update just add, dont subtract.
One dry run should be enough to convince you that its really magical!
Also BIT is 1-based.
public static void update(int x, int val, int size){
//int k =x;
x++;
for (; x<size; x+= x&(-x))
BIT[x]+=val;
}
public static int query(int x){
//int k =x;
int toreturn =0;
for (; x >0; x-= x&(-x))
toreturn+=BIT[x];
return toreturn;
}
public static List<Integer> countSmaller(int[] nums) {
// will only work for positive numbers less that 7.
// I arbitrarily set the size to 7, my code my choice
int size = 7;
BIT = new int[size];
List<Integer> result = new ArrayList<Integer>();
for (int i =nums.length-1; i >=0; i--) {
int smaller_count = query(nums[i]);
result.add(smaller_count);
update(nums[i], 1, size);
}
Collections.reverse(result);
return result;
}