Why does only backtracking work in this scenario? - c++

I am solving this question on LeetCode.com:
In a gold mine grid of size m * n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty. Return the maximum amount of gold you can collect under the conditions:- (a) Every time you are located in a cell you will collect all the gold in that cell; (b) From your position you can walk one step to the left, right, up or down. (c) You can't visit the same cell more than once; (d) Never visit a cell with 0 gold. (e) You can start and stop collecting gold from any position in the grid that has some gold.
For the grid: [[0,6,0],[5,8,7],[0,9,0]] the output is: 24.
I wrote the code below:
class Solution {
public:
int dig(vector<vector<int>>& grid, int i, int j) {
if(i>=grid.size() || i<0 || j>=grid[0].size() || j<0 || grid[i][j]==0) return 0;
//change begins...
int gold=0;
gold+=grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid, i+1, j), max(dig(grid, i, j+1), max(dig(grid, i-1, j), dig(grid, i, j-1))));
return gold;
//change ends...
}
int getMaximumGold(vector<vector<int>>& grid) {
vector<vector<int>> gridCopy=grid;
int maxGold=0;
for(int i=0; i<grid.size(); i++) {
for(int j=0; j<grid[0].size(); j++) {
if(grid[i][j]!=0) {
maxGold=max(maxGold, dig(gridCopy, i, j));
gridCopy=grid;
}
}
}
return maxGold;
}
};
However, it breaks on the input [[1,0,7,0,0,0],[2,0,6,0,1,0],[3,5,6,7,4,2],[4,3,1,0,2,0],[3,0,5,0,20,0]]; yielding 58 instead of 60.
There's another code that I found here, which is identical, except in the commented part above, wherein they have the below lines instead:
g[i][j] = -g[i][j];
auto res = max({ dfs(g, i + 1, j), dfs(g, i, j + 1), dfs(g, i - 1, j), dfs(g, i, j - 1) });
g[i][j] = -g[i][j];
return g[i][j] + res;
(And of course, they don't assign grid to gridCopy in the nested for loop, since they revert the modified grid back to its original form).
I understand they are backtracking, while I am not. But I am unable to understand what I am doing incorrect, since logically, I am doing the exact same thing. I used debug statements to trace the issue, but it is becoming difficult to follow along since there are many recursive calls.
Could someone please point out what is the logical fallacy in my code above?
Thanks!

All your recursive calls might modify the grid, and don't restore it.
That means that the first visit to a cell will block it for all other subsequent attempts.
For instance, if dig(grid, i+1, j) is evaluated first, none of the cells that were visited during that computation are available when you do the other three directions.
If your code happens to start in the top left corner and go downwards first in your example, you will visit 1-2-3-4-3 and get stuck.
After that walk, there is no path from the top left corner any more, even though there were plenty of them from the beginning, and some of them pay a lot more than 13.
(You might think that you're going to find a path from either direction, but it depends on the evaluation order.)
Shared mutable state and recursion is a very tricky combination.

One thing which seems wrong in your code is:
//change begins...
int gold=0;
gold+=grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid, i+1, j), max(dig(grid, i, j+1), max(dig(grid, i-1, j), dig(grid, i, j-1))));
return gold;
//change ends...
Here you have mutated the value of grid[i][j] and that changed value is impacting your input, i.e. your input set is wrong now. Since the value of the grid[i][j] is changed and thus this will impact the rest of the calculation.
What you can do is store the initial value of grid[i][j] somewhere in that recursion stack and reassign back to the grid[i][j] after you are done exploring all paths from that node.
eg: a slight modification in your logic
//change begins...
int gold=0;
gold+=grid[i][j];
int temp = grid[i][j];
grid[i][j]=0;
gold+=max(dig(grid, i+1, j), max(dig(grid, i, j+1), max(dig(grid, i-1, j), dig(grid, i, j-1))));
grid[i][j] = temp;
return gold;
//change ends...
You can also save creating a memory in your recursion stack if you use the solution there in your question.
Just to answer your concern why only backtracking can solve this problem:
You need to understand your solution, which seems like:
look at each grid item if that can be part of the solution set.
for that, you select one grid item (where item value should be greater than 0)
then you explore all surroundings from that item. And using DFS you keep on exploring their surroundings and their surroundings, etc, until the exit condition is met.
Now, in your solution, you have mutated the value of grid[i][j] (for understanding purpose, let us say grid[2][3] is mutated) which is fine if and only if that item selected is present in the final solution set.
But, while exploring other possibilities, if you may happen to find some possibility with a greater number of golds present. Then there also grid[2][3] will be involved. Which you have marked as 0, i.e. the calculation for that node will go wrong.
So, you need to restore the original value to the grid item grid[i][j]. The reason you are making that 0 is you don't want to include it again as you have already visited. But for other solution sets, you need the original value to be present there.

Related

Intuition behind incrementing the iteration variable?

I am solving a question on LeetCode.com:
Given an array with n objects colored red, white or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white and blue. Here, they use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. [The trivial counting sort cannot be used].
For the input: [2,0,2,1,1,0]; the output expected is: [0,0,1,1,2,2].
One of the highly upvoted solutions goes like this:
public void sortColors(vector<int>& A) {
if(A.empty() || A.size()<2) return;
int low = 0;
int high = A.size()-1;
for(int i = low; i<=high;) {
if(A[i]==0) {
// swap A[i] and A[low] and i,low both ++
int temp = A[i];
A[i] = A[low];
A[low]=temp;
i++;low++;
}else if(A[i]==2) {
//swap A[i] and A[high] and high--;
int temp = A[i];
A[i] = A[high];
A[high]=temp;
high--;
}else {
i++;
}
}
}
My question is, why is i incremented when A[i]==0 and A[i]==1 and not when A[i]==2? Using pen and paper, the algorithm just works to give me the answer; but could you please provide some intuition?
Thanks!
This steps through the array and maintains the constraint that the elements 0..i are sorted, and all either 0 or 1. (The 2's that were there get swapped to the end of the array.)
When A[i]==0, you're swapping the element at i (which we just said was 0) with the element at low, which is the first 1-element (if any) in the range 0..i. Hence, after the swap, A[i]==1 which is OK (the constraint is still valid). We can safely move forward in the array now. The same is true if A[i]==1 originally, in which case no swap is performed.
When A[i]==2, you're essentially moving element i (which we just said was 2) to the end of the array. But you're also moving something from the end of the array into element i's place, and we don't know what that element is (because we haven't processed it before, unlike the A[i]==0 case). Hence, we cannot safely move i forward, because the new element at A[i] might not be in the right place yet. We need another iteration to process the new A[i].
That is, because for 0s and 1s, only items left of the current item are handled and those have already been reviewed / sorted. Only for 2s items from the right end of the array are handled, which haven't been looked at yet.
To be more specific: In this specific example only three different states are handled:
the current item being reviewed equals 0: in this case this sorting algorithm just puts this item at the end of all zeros, which have already been sorted (aka A[low]). Also the item which was at A[low] before can only be a 0 or 1 (since they have already sorted) which means you can just swap with the current item and not break the sequence. Now the interesting part: up until now, every item from A[0] over A[low] to A[i] has been already sorted, so the next item which has to be reviewed will be A[i + 1], hence the i++
the current item equals 1: in this case, no swapping has to be done, since all 0s and 1s has already been put in A[0] to A[i - 1] and all 2s have already been put at the end of the array. That means, the next item to be reviewed is A[i + 1], hence the i++
the current item equals 2: in this case, the current item will be put at the end of the array, next to (i.e., to the left of) all the other already sorted 2s (A[high]). The item, which will be swapped from A[high] to A[i] has not been sorted yet and therefor has to be reviewed in the next step, hence th i = i;

Backtracking algorithm gets stuck

I have this problem of a matrix (map) that starting from top-left corner, I want to find the less heavier path to bottom-right corner. It has the condition that it can only move right, down or right-down.
This is an example:
matrix example
I need to solve the problem with backtracking, but I can't tell if I'm doing it well.
This code is able to solve matrix sizes up to 10x10, but when I try a 20x20 matrix, it gets stuck (or at least that's what I think after hours).
/*
* i, j -> matrix iterators.
* n, m -> matrix height and width
* map -> matrix
* actualPath, bestPath -> vectors for representing the path later
* actual -> actual path weight
* best -> best path weight
*/
int backtracking(int i, int j, const int &n, const int &m,
const vector<vector<int>> &map,
vector<vector<int>> &actualPath,
vector<vector<int>> &bestPath,
int best) {
recursiveCalls++;
int actual = 0;
//Bottom-right corner
if(i == (n-1) && j == (m-1)) {
return map[i][j];
}
//Last row, only right
else if(i == (n-1)) {
actual = map[i][j] +
backtracking(i, (j+1), n, m, map, actualPath, bestPath, best, profundidad);
}
//Last column, only down
else if(j == (m-1)) {
actual = map[i][j] +
backtracking((i+1), j, n, m, map, actualPath, bestPath, best, profundidad);
}
else {
int downRight = backtracking((i+1), (j+1), n, m, map, actualPath, bestPath, best, profundidad);
int right = backtracking(i, (j+1), n, m, map, actualPath, bestPath, best, profundidad);
int down = backtracking((i+1), j, n, m, map, actualPath, bestPath, best, profundidad);
actual = map[i][j] + minimo(downRight, right, down);
}
if(actual < best) {
best = actual;
bestPath = actualPath;
}
return best;
}
Is it possible that it gets stuck because I don't use bounds? Or is it bad implemented?
I don't know what I'm doing wrong. I think I understand this algorithm but I guess I don't know how to implement it for this problem...
Although backtracking will give you the correct answer here. It is not the fastest solution in this case.
You are doing a lot duplicate work here, which are not necessary. Straightforward Backtracking is not useful in this case. Lets take a look at this example,
suppose the grid size is 10X10.
one of the trees of the backtrackting stared from (0,0) -> (0,1)
another started from (0,0) -> (1,0)
and another started from (0,0) -> (1,1)
When the 1st traversal reaches point (5,5) it will keep finding all possible ways to go to (9,9). Now the 2nd traversal when that reaches (5,5) it will do the exact same work the first traversal did from this stage, and so will the 3rd traversal. So these duplicate steps are the places where you are exhausting your program and your code is taking way too long to execute. Your code is not stuck its just running for very long time. You can memoize the results easily to optimize the time here.
So if you can save the value that you found when you first reached a point (i,j) as save[i][j], then when some other traversal reaches this same point(i,j) it can decide not to traverse any further and use this save[i][j] for its own. This way you can make the code lot more faster.
This way it will become more of dynamic programming than backtracking, and even a grid of size 10000X10000 will take around few seconds to give you the results.
In this answer I only described how to find the value of the path towards min value, if you want to find the path thats also possible using the same DP solution.

Game of life continues bounds

So I've been trying my hand at game of life and I noticed that the cells only stay confined within the grid that I've created. I want to try and make it continuous so if a cell reaches one side it will continue from the other side. Similar to the game pac-man when you leave from the left to come back into the game from the right side. Here is an image of how it would look as the cell moves out of bounds http://i.stack.imgur.com/dofv6.png
Here is the code that I have which confines everything. So How would I make it wrap back around?
int NeighborhoodSum(int i, int j) {
int sum = 0;
int k, l;
for (k=i-1;k<=i+1;k++) {
for (l=j-1;l<=j+1;l++) {
if (k>=0 && k<gn && l>=0 && l<gm && !(k==i && l==j)) {
sum+=current[k][l];
}
}
}
return sum;
}
Based on dshepherd suggestion this is what I have come up with.
if (!(k == i && l == j)) {
sum += current[k][l];
} else if (k == 1 || k == -1) { // rows
sum += current[k+1][l];
} else if (l == 1 || l == -1) { // columns
sum += current[k][l+1];
}
Start considering a one dimension array, of size ARRAY_SIZE.
What do you want that array to return when you ask for a cell of a negative index ? What about a for an index >= ARRAY_SIZE ? What operators does that make you think of (hint : <= 0, % ARRAY_SIZE, ...)
This will lead you to a more generic solution that dshepherd's one, for example if you want in the future to be able to specify life / death rules more than just one index around the cell.
Assuming a grid size of 0 to n-1 for x and 0 to m-1 for y (where n is the size of the x dimension and m is the size of the y dimension, what you want to do is check if the coordinates are in-range, and move accordingly. So (pseudocode):
// normal move calculation code here
if (x < 0) { x = n-1; }
if (x >= n) { x = 0; }
if (y < 0) { y = m-1; }
if (y >= m) { y = 0; }
// carry out actual move here
With the start position marked as red, you need to calculate a movement into, or a breeding into, a new square: you need to check for whether it would fall out of bounds. If it does then the new cell would be born in either of the orange positions, if not it could be born
in any of the blue positions:
Hope that helps:) Let me know if you need more information though:)
It looks like you are taking a summation over nearest neighbours, so all you need to do to make it wrap around is to extend the summation to include the cells on the other side if (i,j) is an edge cell.
You could do this fairly easily by adding else statements to the central if to check for the cases where l or k are -1 or gn/gm (i.e. just past the edges) and add the appropriate term from the cell on the opposite side.
Update:
What you've added in your edit is not what I meant, and I'm pretty sure it won't work. I think you need to carefully think through exactly what it is that the initial code does before you go any further. Maybe get some paper and do some example cases by hand?
More specific advice (but do what I said above before you try to use this):
You can't directly take current[k][l] if k or l are negative or greater than gn/gm respectively because there is no array entry with that index (the program should segfault). What you actually want it to do is use the 0th entry anywhere that it would normally use the gnth entry, and so on for all the other boundaries.
You will probably need to split the if statement into 5 parts not 3 because the cases for k < 0 and k > gn are different (similarly for l).
You are comparing against completely the wrong values with (k == 1 || k == -1) and similarly for l

Algorithm for finding the maximum number of non-overlapping lines on the x axis

I'm not exactly sure how to ask this, but I'll try to be as specific as possible.
Imagine a tetris screen with only rectangles, of different shapes, falling to the bottom.
I want to compute the maximum number of rectangles that I can fit one next to the other without any overlapping ones. I've named them lines in the title because I'm actually only interested in the length of the rectangle when computing, or the line parallel to the x axis that it's falling towards.
So basically I have a custom type with a start and end, both integers between 0 and 100. Say we have a list of these rectangles ranging from 1 to n. rectangle_n.start (unless it's the rectangle closest to the origin) has to be > rectangle_(n-1).end so that they will never overlap.
I'm reading the rectangle coordinates (both are x axis coordinates) from a file with random numbers.
As an example:
consider this list of rectangle type objects
rectangle_list {start, end} = {{1,2}, {3,5}, {4,7} {9,12}}
We can observe that the 3rd object has its start coordinate 4 < the previous rectangle's end coordinate which is 5. So in sorting this list, I would have to remove the 2nd or the 3rd object so that they don't overlap.
I'm not sure if there is a type for this kind of problem so I didn't know how else to name it. I'm interested in an algorithm that can be applied on a list of such objects and would sort them out accordingly.
I've tagged this with c++ because the code I'm writing is c++ but any language would do for the algorithm.
You are essentially solving the following problem. Suppose we have n intervals {[x_1,y_1),[x_2,y_2),...,[x_n,y_n)} with x_1<=x_2<=...<=x_n. We want to find a maximal subset of these intervals such that there are no overlaps between any intervals in the subset.
The naive solution is dynamic programming. It guarantees to find the best solution. Let f(i), 0<=i<=n, be the size of the maximal subset up to interval [x_i,y_i). We have equation (this is latex):
f(i)=\max_{0<=j<i}{f(j)+d(i,j)}
where d(i,j)=1 if and only if [x_i,y_i) and [x_j,y_j) have no overlaps; otherwise d(i,j) takes zero. You can iteratively compute f(i), starting from f(0)=0. f(n) gives the size of the maximal subset. To get the actual subset, you need to keep a separate array s(i)=\argmax_{0<=j<i}{f(j)+d(i,j)}. You then need to backtrack to get the 'path'.
This is an O(n^2) algorithm: you need to compute each f(i) and for each f(i) you need i number of tests. I think there should be a O(nlogn) algorithm, but I am not so sure.
EDIT: an implementation in Lua:
function find_max(list)
local ret, f, b = {}, {}, {}
f[0], b[0] = 0, 0
table.sort(list, function(a,b) return a[1]<b[1] end)
-- dynamic programming
for i, x in ipairs(list) do
local max, max_j = 0, -1
x = list[i]
for j = 0, i - 1 do
local e = j > 0 and list[j][2] or 0
local score = e <= x[1] and 1 or 0
if f[j] + score > max then
max, max_j = f[j] + score, j
end
end
f[i], b[i] = max, max_j
end
-- backtrack
local max, max_i = 0, -1
for i = 1, #list do
if f[i] > max then -- don't use >= here
max, max_i = f[i], i
end
end
local i, ret = max_i, {}
while true do
table.insert(ret, list[i])
i = b[i]
if i == 0 then break end
end
return ret
end
local l = find_max({{1,2}, {4,7}, {3,5}, {8,11}, {9,12}})
for _, x in ipairs(l) do
print(x[1], x[2])
end
The name of this problem is bin packing, it is usually considered as a hard problem but can be computed reasonably well for small number of bins.
Here is a video explaining common approaches to this problem
EDIT : By hard problem, I mean that some kind of brute force has to be employed. You will have to evaluate a lot of solutions and reject most of them, so usually you need some kind of evaluation mechanism. You need to be able to compare solution, such as "This solution packs 4 rectangles with area of 15" is better than "This solution packs 3 rectangles with area of 16".
I can't think of a shortcut, so you may have to enumerate the power set in descending order of size and stop on the first match.
The straightforward way to do this is to enumerate combinations of decreasing size. You could do something like this in C++11:
template <typename I>
std::set<Span> find_largest_non_overlapping_subset(I start, I finish) {
std::set<Span> result;
for (size_t n = std::distance(start, finish); n-- && result.empty();) {
enumerate_combinations(start, finish, n, [&](I begin, I end) {
if (!has_overlaps(begin, end)) {
result.insert(begin, end);
return false;
}
return true;
});
}
return result;
}
The implementation of enumerate_combination is left as an exercise. I assume you already have has_overlap.

How can I find number of consecutive sequences of various lengths satisfy a particular property?

I am given a array A[] having N elements which are positive integers
.I have to find the number of sequences of lengths 1,2,3,..,N that satisfy a particular property?
I have built an interval tree with O(nlogn) complexity.Now I want to count the number of sequences that satisfy a certain property ?
All the properties required for the problem are related to sum of the sequences
Note an array will have N*(N+1)/2 sequences. How can I iterate over all of them in O(nlogn) or O(n) ?
If we let k be the moving index from 0 to N(elements), we will run an algorithm that is essentially looking for the MIN R that satisfies the condition (lets say I), then every other subset for L = k also is satisfied for R >= I (this is your short circuit). After you find I, simply return an output for (L=k, R>=I). This of course assumes that all numerics in your set are >= 0.
To find I, for every k, begin at element k + (N-k)/2. Figure out if this defined subset from (L=k, R=k+(N-k)/2) satisfies your condition. If it does, then decrement R until your condition is NOT met, then R=1 is your MIN (your could choose to print these results as you go, but they results in these cases would be essentially printed backwards). If (L=k, R=k+(N-k)/2) does not satisfy your condition, then INCREMENT R until it does, and this becomes your MIN for that L=k. This degrades your search space for each L=k by a factor of 2. As k increases and approaches N, your search space continuously decreases.
// This declaration wont work unless N is either a constant or MACRO defined above
unsigned int myVals[N];
unsigned int Ndiv2 = N / 2;
unsigned int R;
for(unsigned int k; k < N; k++){
if(TRUE == TESTVALS(myVals, k, Ndiv2)){ // It Passes
for(I = NDiv2; I>=k; I--){
if(FALSE == TESTVALS(myVals, k, I)){
I++;
break;
}
}
}else{ // It Didnt Pass
for(I = NDiv2; I>=k; I++){
if(TRUE == TESTVALS(myVals, k, I)){
break;
}
}
}
// PRINT ALL PAIRS from L=k, from R=I to R=N-1
if((k & 0x00000001) == 0) Ndiv2++;
} // END --> for(unsigned int k; k < N; k++)
The complexity of the algorithm above is O(N^2). This is because for each k in N(i.e. N iterations / tests) there is no greater than N/2 values for each that need testing. Big O notation isnt concerned about the N/2 nor the fact that truly N gets smaller as k grows, it is concerned with really only the gross magnitude. Thus it would say N tests for every N values thus O(N^2)
There is an Alternative approach which would be FASTER. That approach would be to whenever you wish to move within the secondary (inner) for loops, you could perform a move have the distance algorithm. This would get you to your O(nlogn) set of steps. For each k in N (which would all have to be tested), you run this half distance approach to find your MIN R value in logN time. As an example, lets say you have a 1000 element array. when k = 0, we essentially begin the search for MIN R at index 500. If the test passes, instead of linearly moving downward from 500 to 0, we test 250. Lets say the actual MIN R for k = 0 is 300. Then the tests to find MIN R would look as follows:
R=500
R=250
R=375
R=312
R=280
R=296
R=304
R=300
While this is oversimplified, your are most likely going to have to optimize, and test 301 as well 299 to make sure youre in the sweet spot. Another not is to be careful when dividing by 2 when you have to move in the same direction more than once in a row.
#user1907531: First of all , if you are participating in an online contest of such importance at national level , you should refrain from doing this cheap tricks and methodologies to get ahead of other deserving guys. Second, a cheater like you is always a cheater but all this hampers the hard work of those who have put in making the questions and the competitors who are unlike you. Thirdly, if #trumetlicks asks you why haven't you tagged the ques as homework , you tell another lie there.And finally, I don't know how could so many people answer this question this cheater asked without knowing the origin/website/source of this question. This surely can't be given by a teacher for homework in any Indian school. To tell everyone this cheater has asked you the complete solution of a running collegiate contest in India 6 hours before the contest ended and he has surely got a lot of direct helps and top of that invited 100's others to cheat from the answers given here. So, good luck to all these cheaters .