Click here to Skip to main content
15,921,697 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have wrote a function using memoization to count the maximum number of paths having the maximum sum from top left to bottom right of a matrix.My code is working fine for all the testcases except the below case which is showing incorrect output.Can anyone please tell me the logic that is missing in my code?

n-->15(size of matrix)
max_path_sum -->143(maximum sum from top left to bottom right)

e 1 1 7 x 7 6 8 4 2 2 7 3 4 8
5 2 3 x 2 5 2 3 8 x 8 2 x 8 1
6 3 1 x 2 9 9 4 7 x 9 8 1 8 6
8 1 x x 9 x 8 6 3 5 2 x 4 9 4
5 5 3 5 5 9 x 3 8 9 3 1 1 x 5
3 6 1 1 x x 2 3 x 3 7 9 4 9 2
6 9 1 8 x x 5 3 2 1 2 9 x 7 x
4 1 x 1 8 1 2 9 6 5 6 1 1 x 2
x 6 4 3 9 2 5 x 7 x 2 8 6 x x
8 6 x x 7 1 3 8 x 5 3 x x 6 7
4 5 6 5 6 1 9 x 3 4 2 2 2 2 8
x 7 5 2 2 8 x x 4 5 3 5 8 x 1
9 5 9 5 x 4 8 3 x x 5 6 1 2 6
1 4 x x x x 4 3 2 1 5 8 2 x 4
6 5 2 7 4 6 4 7 3 x 4 8 x 7 s


C++
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char a[100][100];
int i,j,n,max_path_sum,max_path_count;

int isvalidate(int i, int j,int c)
{
    if(i >= 0 && i < n && j>=0 && j<n &&="" c="">=0 && c <= max_path_sum)
        return 1;
    else 
        return 0;
    
}
  
int max(int a,int b, int c)
{
     if(a >= b && a >= c)
         return a;
     else if(b >= a && b >= c)
         return b;
     else 
         return c;
}
/* Memoization approach to find the number of paths having the maximum sum */

int maxcountPath(int i,int j,int c)
{   
    int countDP[n][n][max_path_sum];
    
	if(i==0 && j==0 && c== 0)
	{   
		if(((a[i][j]-'0') - c) ==0)
			return countDP[i][j][c] = 1;
		else
			return countDP[i][j][c] = 0;
	}
	
	if(i==0 && isvalidate(i,j,c) && a[i][j] != 'x')
		return countDP[i][j][c] = maxcountPath(i,j-1,c-(a[i][j]-'0'));
	  
	else if(j==0 && isvalidate(i,j,c) && a[i][j] != 'x')
		return countDP[i][j][c] = maxcountPath(i-1,j,c-(a[i][j]-'0'));
    
    else if(isvalidate(i,j,c))
	    return countDP[i][j][c] = maxcountPath(i,j-1,c-(a[i][j]-'0')) + 
		  maxcountPath(i-1,j,c-(a[i][j]-'0')) + maxcountPath(i-1,j-1,c-(a[i][j]-'0'));
	else
	    return countDP[i][j][c] = 0;

}
/* DP approach to find the maximum sum path from top left corner to bottom right corner */

int maxsumPath(char a[][100], int n)
{   
    int maxpathDP[n][n];
    
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
        {
            maxpathDP[i][j] = 0;
        }
    }
    
    for(i = 1; i < n; i++)
    {
        if(a[i][0] != 'x')
            maxpathDP[i][0] = a[i][0]-'0' +maxpathDP[i-1][0];
        else
            break;
    
    }
    
    for(j = 1; j < n; j++)
    {
        if(a[0][j] != 'x')
            maxpathDP[0][j] = a[0][j]-'0'+maxpathDP[0][j-1];
        else
            break;
    }
    
    for(i = 1; i < n; i++)
    {
        for(j = 1; j < n; j++)
        {
            if(a[i][j] == 'x')
            {    
                continue;
            }
            else
            {
                if(a[i][j] == 's')
                {   
                    maxpathDP[i][j] = max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]); 
                }
                else
                {
                 
                    maxpathDP[i][j] = a[i][j]-'0' + max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]);
                }
            }
        }
    }
    
   return maxpathDP[n-1][n-1];
    
}
 
int main()
{   
    int t;
    scanf("%d", &t);

    while(t--)
    {   
        scanf("%d", &n);
      
        for (i = 0; i < n; i++)
        {
            for (j = 0; j < n; j++)
            {
                scanf(" %c", &a[i][j]);
            }
        }
        
        max_path_sum = maxsumPath(a,n);
        
        a[0][0] = '0';
        a[n-1][n-1] = '0';
        
        max_path_count = maxcountPath(n-1,n-1,max_path_sum);
        
        if(max_path_count == 0)
		{
            max_path_sum = 0;
        }
        printf("%d %d\n",max_path_sum,max_path_count);
    }
    
    return 0;
}


What I have tried:

Intially i was facing SIGSEGV which is fixed by validating the boundary conditions
Posted
Updated 13-Jun-19 3:30am
v4
Comments
Patrice T 12-Jun-19 1:52am    
What result do you get and what result do you expect ?
[no name] 12-Jun-19 3:34am    
----INPUT-----
5
15
e 1 1 7 x 7 6 8 4 2 2 7 3 4 8
5 2 3 x 2 5 2 3 8 x 8 2 x 8 1
6 3 1 x 2 9 9 4 7 x 9 8 1 8 6
8 1 x x 9 x 8 6 3 5 2 x 4 9 4
5 5 3 5 5 9 x 3 8 9 3 1 1 x 5
3 6 1 1 x x 2 3 x 3 7 9 4 9 2
6 9 1 8 x x 5 3 2 1 2 9 x 7 x
4 1 x 1 8 1 2 9 6 5 6 1 1 x 2
x 6 4 3 9 2 5 x 7 x 2 8 6 x x
8 6 x x 7 1 3 8 x 5 3 x x 6 7
4 5 6 5 6 1 9 x 3 4 2 2 2 2 8
x 7 5 2 2 8 x x 4 5 3 5 8 x 1
9 5 9 5 x 4 8 3 x x 5 6 1 2 6
1 4 x x x x 4 3 2 1 5 8 2 x 4
6 5 2 7 4 6 4 7 3 x 4 8 x 7 s
9
e 4 x 9 1 7 5 9 1
2 9 8 2 9 6 x 8 8
9 5 9 5 7 1 x 2 1
2 3 8 9 x 3 8 7 8
8 8 9 2 x 2 7 8 2
4 6 2 6 8 7 9 5 9
x 6 3 8 8 3 5 8 7
9 5 7 3 5 8 4 8 1
x 4 4 5 8 7 4 1 s
7
e 2 8 7 6 7 4
2 6 6 2 x 6 7
3 1 1 4 x 7 2
1 4 2 6 1 7 6
x 7 8 9 x 4 x
7 1 1 4 x 2 4
8 6 5 9 1 1 s
7
e 5 4 9 9 3 7
x 3 1 4 5 7 5
3 6 3 x 6 x 5
7 1 5 8 x 9 1
8 4 3 9 6 8 3
2 x x 5 9 3 7
3 2 6 4 7 4 s
3
e 6 1
4 8 9
9 9 s
----------OUTPUT-------------------
143 ?
101 15
60 1
65 3
23 2
Patrice T 12-Jun-19 2:13am    
Advice: show enough code so we can run it.
[no name] 12-Jun-19 3:32am    
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char a[100][100];
int i,j,n,max_path_sum,max_path_count;

int isvalidate(int i, int j,int c)
{
if(i >= 0 && i < n && j>=0 && j<n &&="" c="">=0 && c <= max_path_sum)
return 1;
else
return 0;

}

int max(int a,int b, int c)
{
if(a >= b && a >= c)
return a;
else if(b >= a && b >= c)
return b;
else
return c;
}
/* Memoization approach to find the number of paths having the maximum sum */

int maxcountPath(int i,int j,int c)
{
int countDP[n][n][max_path_sum];

if(i==0 && j==0 && c== 0)
{
if(((a[i][j]-'0') - c) ==0)
return countDP[i][j][c] = 1;
else
return countDP[i][j][c] = 0;
}

if(i==0 && isvalidate(i,j,c) && a[i][j] != 'x')
return countDP[i][j][c] = maxcountPath(i,j-1,c-(a[i][j]-'0'));

else if(j==0 && isvalidate(i,j,c) && a[i][j] != 'x')
return countDP[i][j][c] = maxcountPath(i-1,j,c-(a[i][j]-'0'));

else if(isvalidate(i,j,c))
return countDP[i][j][c] = maxcountPath(i,j-1,c-(a[i][j]-'0')) +
maxcountPath(i-1,j,c-(a[i][j]-'0')) + maxcountPath(i-1,j-1,c-(a[i][j]-'0'));
else
return countDP[i][j][c] = 0;

}
/* DP approach to find the maximum sum path from top left corner to bottom right corner */

int maxsumPath(char a[][100], int n)
{
int maxpathDP[n][n];

for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
maxpathDP[i][j] = 0;
}
}

for(i = 1; i < n; i++)
{
if(a[i][0] != 'x')
maxpathDP[i][0] = a[i][0]-'0' +maxpathDP[i-1][0];
else
break;

}

for(j = 1; j < n; j++)
{
if(a[0][j] != 'x')
maxpathDP[0][j] = a[0][j]-'0'+maxpathDP[0][j-1];
else
break;
}

for(i = 1; i < n; i++)
{
for(j = 1; j < n; j++)
{
if(a[i][j] == 'x')
{
continue;
}
else
{
if(a[i][j] == 's')
{
maxpathDP[i][j] = max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]);
}
else
{

maxpathDP[i][j] = a[i][j]-'0' + max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]);
}
}
}
}

return maxpathDP[n-1][n-1];

}

int main()
{
int t;
scanf("%d", &t);

while(t--)
{
scanf("%d", &n);

for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
scanf(" %c", &a[i][j]);
}
}

max_path_sum = maxsumPath(a,n);

a[0][0] = '0';
a[n-1][n-1] = '0';

max_path_count = maxcountPath(n-1,n-1,max_path_sum);

if(max_path_count == 0)
{
max_path_sum = 0;
}
printf("%d %d\n",max_path_sum,max_path_count);
}

return 0;
}
CPallini 12-Jun-19 7:28am    
Please post the exact code. This line
if(i >= 0 && i < n && j>=0 && j<n &&="" c="">=0 && c <= max_path_sum)

won't even compile.

Just some things to simplify your code and maybe make it clearer what is happening, and, more importantly, not happening:

1. All the return statements in your function maxcountpath(...) assign a value to the local array countDP and then return that value. Since countDP will no longer be used after the return, the assignment is meaningless. You should therefore just return the assigned value instead!

2. Once you remove the assignments, you'll realize you are never using countDP. You can eliminate that array entirely.

3. If this condition:
if(i==0 && j==0 && c== 0)
is true, it means you can use 0 instead of i and j in the following branch. This simplifies the following if in such a way that you will realize it is always true.

4. You check two things at the same time: whether you've reached the starting point (0,0), and whether you've found a path with the right sum (c==0). This makes it harder to understand what happens for various combinations of these two conditions. You should split them into separate checks.

5. You repeatedly check whether your current cell is 'x'. Instead you could just check once and return - you can not take a path using this cell!

6. You repeatedly convert the numeric value of the cell a[i][j], which makes your code a bit harder to read. Just convert that value once and store it in a local variable.

7. It's a good idea to validate your input, but why do that repeatedly in every if branch? Do it right at the start of the function, once! Then you don't need to check later.

These things simplify your function to:
C++
int maxcountPath(int i,int j,int c)
{
	// have we gone outside the bounds?
	if (!isvalidate(i,j,c))
		return 0;
	// after this point, you know your input is fine!

	// has the recursion reached the starting point?
	if(i==0 && j==0) // first part of your orginial check
	{   
		if (c==0) // second part
			return 1;
		else // this is new! If c!=0, your path is not correct!
			return 0;
	}
	// after this point, you know you're not at the starting point yet

	// do we have a valid cell?
	if (a[i][j] == 'x')
		return 0; // you shall not pass!
	// from this point we know we have a valid cell

	// convert and store current cell value:
	int cell_value = a[i][j]-'0';

	if(i==0) // are we in first row?
		return maxcountPath(i,j-1,c-cell_value);
	  
	else if(j==0) // are we in first column?
		return maxcountPath(i-1,j,c-cell_value));
    
	return maxcountPath(i,j-1,c-cell_value) + 
		maxcountPath(i-1,j,c-cell_value) + maxcountPath(i-1,j-1,c-cell_value);

}

On a sidenote, these simplifications (mostly the early returns) probably avoid the issue of i or j going below 0, therefore you can simplify your isvalidate() function to just check c. (and since c can only go out of bounds if you pass the wrong value from main(), you can probably remove that isvalidate() function entirely)
 
Share this answer
 
v2
Comments
[no name] 12-Jun-19 8:30am    
Thanks for suggestion.
Recursion approach will work fine for small inputs, but it will take exponetial time if we have a very large input.How can i change this recursive approach to DP
Stefan_Lang 13-Jun-19 2:55am    
Initially I also thought to avoid the recursion, but then i realized that, for an input of size NxN, the maximum rexursion depth is only 2*N - that's not so bad for N<1000, although you may need to increase the program stack size.

If you want to avoid the recursion, I'd better post a separate solution with explanations for that, for better readability.
[no name] 13-Jun-19 3:03am    
Thanks for the reply.
Please explain the equivalent dynamic programming approach for the same recursion function.I tried to implement it using DP,but was getting incorrect output.


Rick York 12-Jun-19 10:49am    
One more suggestion : save the values in binary form, not ASCII. This way you don't have to convert them to their binary value so often. You can convert them once and store them like that. Since x is a special value you can save it in its binary form also which is 120 and that should probably get a constant definition. This means your values will range from 0 to 9 and then the X value, 120 - all in binary.
[no name] 12-Jun-19 15:33pm    
I have improved recursion to memoization.How can i convert maxcountPath() to dynamic programming?

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char a[100][100];
int i,j,n,max_path_sum,max_path_count;

int isvalidate(int i, int j,int c)
{
if(i >= 0 && i < n && j>=0 && j<n &&="" c="">=0 && c <= max_path_sum)
return 1;
else
return 0;

}

int max(int a,int b, int c)
{
if(a >= b && a >= c)
return a;
else if(b >= a && b >= c)
return b;
else
return c;
}

int maxcountPath(int i,int j,int c)
{
int countDP[n][n];
int cell_value = a[i][j]-'0';

if (!isvalidate(i,j,c))
return countDP[i][j] = 0;

if(i==0 && j==0)
{
if (c==0)
return countDP[i][j] = 1;
else
return countDP[i][j] = 0;
}


if (a[i][j] == 'x')
return countDP[i][j] = 0;

if(i==0)
return countDP[i][j] = maxcountPath(i,j-1,c-cell_value);

else if(j==0)
return countDP[i][j] = maxcountPath(i-1,j,c-cell_value);

return countDP[i][j] = maxcountPath(i,j-1,c-cell_value) +
maxcountPath(i-1,j,c-cell_value) + maxcountPath(i-1,j-1,c-cell_value);

}

int maxsumPath(char a[][100], int n)
{
int maxpathDP[n][n];

for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
maxpathDP[i][j] = 0;
}
}

for(i = 1; i < n; i++)
{
if(a[i][0] != 'x')
maxpathDP[i][0] = a[i][0]-'0' +maxpathDP[i-1][0];
else
break;

}

for(j = 1; j < n; j++)
{
if(a[0][j] != 'x')
maxpathDP[0][j] = a[0][j]-'0'+maxpathDP[0][j-1];
else
break;
}

for(i = 1; i < n; i++)
{
for(j = 1; j < n; j++)
{
if(a[i][j] == 'x')
{
continue;
}
else
{
if(a[i][j] == 's')
{
maxpathDP[i][j] = max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]);
}
else
{

maxpathDP[i][j] = a[i][j]-'0' + max(maxpathDP[i-1][j-1],maxpathDP[i-1][j],maxpathDP[i][j-1]);
}
}
}
}

return maxpathDP[n-1][n-1];

}

int main()
{
int t;
scanf("%d", &t);

while(t--)
{
scanf("%d", &n);

for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
scanf(" %c", &a[i][j]);
}
}

max_path_sum = maxsumPath(a,n);

a[0][0] = '0';
a[n-1][n-1] = '0';

max_path_count = maxcountPath(n-1,n-1,max_path_sum);

if(max_path_count == 0)
{
max_path_sum = 0;
}
printf("%d %d\n",max_path_sum,max_path_count);
}

return 0;
}
As per request, here's how you need to go about to create a non-recursive solution:

There are probably many different approaches, but the easiest to explain in this context, is simply turning the recursion upside-down:

The recursion works by calculating the results for the smaller sized problem, and deriving the solution for the full-sized problem from that. The iterative solution reverts this approach, by solving the smallest size problems, and iteratively derive the solutions for the next larger sized problems. Obviously, you need to store the intermediate results somehow, and you need to make sure that you get all the smaller solutions that you need in the next step.

For this problem, the partial problems to solve iteratively are the maximum value and number of maximum paths from one of the end points to one position in the NxN matrix. To keep track of these two values, I'd suggest creating a simple struct. And to keep track of all partial solutions, I'd create a NxN array of these structs:
C++
struct sum_paths {
    int sum;
    int num_paths;
};
struct path_results {
    int rows;
    int columns;
    sum_paths* results;// must be allocated dynamically (and released when done)

    // get index for array element
    int at (int row, int column)
    {
        return row*columns+column;
    }

    // set values at array position
    void set_at(int row, int column, int sum, int npaths)
    {
        results[at(row, column)].sum = sum;
        results[at(row, column)].num_paths = npaths;
    }

    // retrieve values at array position
    sum_paths get_at(int row, int column)
    {
        return results[at(row, column)];
    }
};
I've added a few utility functions to simplify reading and writing.

You'll start in one corner - in the spirit of your recursive program that roots its recursion in the lower right corner, I'd suggest starting in the lower right corner. The maximum value to reach that position is 0, because there is no value assigned to this cell, and the number of paths is 1, so you assign (0,1) to the result array at position (N-1,N-1) :
C++
// allocate matrix for intermediate results
path_results results;
// to do: allocate results array

// set position at end of path
int row = N-1;
int column = N-1;

// initialize end of path (sum = 0 and num_paths = 1)
results.set_at(row, column, 0, 1);


Next you can derive the correct values for the bottom row, going right to left. You need to be careful that you should only set a value != 0 here if there is actually a path leading to the cell to your right, and the current cell is not blocked:
C++
for (column = N-2; column >= 0; --column)
{
    int sum = 0;
    int npaths = 0;
    if (A[row][column] != 'x')
    {
        npaths = results.get_at(row, column+1).num_paths;
        if (npaths > 0)
            sum = A[row][column]-'0' + results.get_at(row, column+1).sum;
    }
    results.set_at(row, column, sum, npaths);
}

Next you continue from bottom to top and right to left, to fill in the results set. You have to be careful when checking and combining the results from the cells to your right, from below, and from the lower right: first you need to find out which path yields the maximum sum, and then you need to add the paths from each direction that does yield this result. And of course you always need to check whether there is a valid path in either direction to start with.

Eventually your process will reach cell [0][0], and you can print out the result.

I'll leave the details of the implementation as an exercise for you. If you don't get the result 4, I suggest that you use the debugger on the last example to find out what is going wrong. (and yes, the fact that I know the result means I do have a working program - but you really should try this on your own)


P.S.:
Here's the code I used to print out not only the final result, but also the entire array of intermediate results. I've found it rather helpful to locate some subtle bugs I've introduced in early versions of my code. You might find it helpful, too:
C++
for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j)
        cout << results.get_at(i, j).sum << ' ';
    cout << endl;
}
cout << endl;
for (int i = 0; i < N; ++i) {
    for (int j = 0; j < N; ++j)
        cout << results.get_at(i, j).num_paths << ' ';
    cout << endl;
}
cout << endl;
cout << "maximum sum: " << results.get_at(0,0).sum << endl;
cout << "number of paths: " << results.get_at(0,0).num_paths << endl;
 
Share this answer
 
v2
If you want people to look at your code, make it easy to read. That means that "lazy variable names" are a poor idea - and single character names are just that.
It means not writing code like "if (...) return 1; else return 0;"
It means documenting your code so people have some idea what you intended code to do.
It means explaining what you expected the code to do, and what exactly it id instead: "it didn't work" is a useless error report because it tells us nothing we didn't already know: if it worked you wouldn't be asking the question!
It means explaining what you have tried to find out what the problem is.

And most of all, it means understanding the development process ... Compiling does not mean your code is right! :laugh:
Think of the development process as writing an email: compiling successfully means that you wrote the email in the right language - English, rather than German for example - not that the email contained the message you wanted to send.

So now you enter the second stage of development (in reality it's the fourth or fifth, but you'll come to the earlier stages later): Testing and Debugging.

Start by looking at what it does do, and how that differs from what you wanted. This is important, because it give you information as to why it's doing it. For example, if a program is intended to let the user enter a number and it doubles it and prints the answer, then if the input / output was like this:
Input   Expected output    Actual output
  1            2                 1
  2            4                 4
  3            6                 9
  4            8                16
Then it's fairly obvious that the problem is with the bit which doubles it - it's not adding itself to itself, or multiplying it by 2, it's multiplying it by itself and returning the square of the input.
So with that, you can look at the code and it's obvious that it's somewhere here:
C#
int Double(int value)
   {
   return value * value;
   }

Once you have an idea what might be going wrong, start using the debugger to find out why. Put a breakpoint on the first line of the method, and run your app. When it reaches the breakpoint, the debugger will stop, and hand control over to you. You can now run your code line-by-line (called "single stepping") and look at (or even change) variable contents as necessary (heck, you can even change the code and try again if you need to).
Think about what each line in the code should do before you execute it, and compare that to what it actually did when you use the "Step over" button to execute each line in turn. Did it do what you expect? If so, move on to the next line.
If not, why not? How does it differ?
Hopefully, that should help you locate which part of that code has a problem, and what the problem is.
This is a skill, and it's one which is well worth developing as it helps you in the real world as well as in development. And like all skills, it only improves by use!
 
Share this answer
 
Quote:
Can anyone please tell me the logic that is missing in my code?

I think your problem is that maxsumPath can take into account some impossible path.
You should print maxpathDP to see how you get the maximum path.

Your code do not behave the way you expect, or you don't understand why !

There is an almost universal solution: Run your code on debugger step by step, inspect variables.
The debugger is here to show you what your code is doing and your task is to compare with what it should do.
There is no magic in the debugger, it don't know what your code is supposed to do, it don't find bugs, it just help you to by showing you what is going on. When the code don't do what is expected, you are close to a bug.
To see what your code is doing: Just set a breakpoint and see your code performing, the debugger allow you to execute lines 1 by 1 and to inspect variables as it execute.

Debugger - Wikipedia, the free encyclopedia[^]

Mastering Debugging in Visual Studio 2010 - A Beginner's Guide[^]
Basic Debugging with Visual Studio 2010 - YouTube[^]

1.11 — Debugging your program (stepping and breakpoints) | Learn C++[^]

The debugger is here to only show you what your code is doing and your task is to compare with what it should do.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900