Maximum sum path in a matrix from top to bottom - c#

Consider a n*m matrix. Suppose each cell in the matrix has a value assigned. We can start from each cell in first row in matrix. The allowed moves are diagonally left, downwards or diagonally right, i.e, from location (i, j) next move can be (i+1, j), or, (i+1, j+1), or (i+1, j-1). (If index is not outside the bounds of the array of course)
Let an additional restriction be added: only paths are allowed that pass (at least once) through all the columns.
Find the maximum sum of elements satisfying the allowed moves.
For example for matrix:
1 15 2
9 7 5
9 2 4
6 9 -1
The sum is equal:
28
Because the path is 15+5+2+6=28.
The main feature is that I need to use a dynamic approach. For a task without restriction about all the columns I could do:
var matrix = new int[,]{ { 1, 15, 2 }, //start matrix
{ 9, 7, 5 },
{ 9, 2, 4},
{ 6, 9, -1 } };
long n = matrix.GetLength(0);
long m = matrix.GetLength(1);
var sum = new List<long[]>(); // list [n][m] of maxsums
for (int i = 0; i < n; i++)
{
sum.Add(new long[m].Select(e => e = long.MinValue).ToArray());
}
for (int i = 0; i < m; i++)
{
sum[0][i] = matrix[0, i]; //sums at first line equal first line in matrix
}
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < m; j++)
{
if (j > 0) sum[i + 1][j - 1] = Math.Max(sum[i][j] + matrix[i + 1, j - 1], sum[i + 1][j - 1]); // diagonally left
sum[i + 1][j] = Math.Max(sum[i][j] + matrix[i + 1, j], sum[i + 1][j]); // downwards
if (j < m - 1) sum[i + 1][j + 1] = Math.Max(sum[i][j] + matrix[i + 1, j + 1], sum[i + 1][j + 1]); //diagonally right
}
}
long max = sum[(int)n - 1].Max(); //maximum sum among all paths (answer)
And for the same matrix the maximum sum will equal:
42
Because the path is 15+9+9+9=42
Нow I can calculate a dynamics matrix for all paths and sums with restriction?

An easy way to do this is with a Queue.
Add the first row
for every item in the queue, add any other valid moves
when finished, check if it's a valid combination
check against the last highest
It uses an and Iterator method, Linq, and queues to return current best finds. What I suggest you do, is research the parts involved, step through it and inspect variables, use Console.WriteLine to look at what is happening. If you are really stuck you can always ask further questions about this code and what it's doing.
The idea of the queue is, we add each element in the first row as initial items in the queue (that is our precondition by the rules you have given), then we go and look at the first element in the queue, then from that position (x,y) we go through all the next positions in the next row that we can legitimately visit. The queue also hold a list of columns visited and a value at that position. It could be done differently. I.e we really only need to to know the sum of all elements visited and a list of columns etc so we can validate the path afterwards.
Note : This is not the most optimal solution and it could be done a lot more efficiently and in less code in many other ways (and more elegantly). However, it touches on a lot of common concepts that are worth understanding
Given
private static Random _rand = new Random();
// this could be done with linq, however it's easy to see how it works
private static bool IsPathValid(int length, List<int> path)
{
for (var i = 0; i < length; i++)
if (!path.Contains(i))
return false;
return true;
}
Iterator
public static IEnumerable<IEnumerable<(int col, int value)>> FindPath(int[, ] matrix)
{
var queue = new Queue<(int x, int y, List<(int col, int value)> path)>();
// add the first row to the queue
for (var i = 0; i < matrix.GetLength(1); i++)
queue.Enqueue((i, 0, new List<(int col, int value)>()));
// lets keep the higest found
var highest = int.MinValue;
// loop all queue items until none left
while (queue.Any())
{
// get the next item out of the queue
var(x, y, path) = queue.Dequeue();
// add the path we are visiting
path.Add((x, matrix[y, x]));
// if we have looked at all the rows, then time to return
if (y + 1 == matrix.GetLength(0))
{
// get a list of columns visited
var cols = path.Select(x => x.col).ToList();
// check to see if all columns are visited
if (IsPathValid(matrix.GetLength(1), cols))
{
var sum = path.Sum(x => x.value);
// sum the path, if it's not the highest we don't care
if (sum > highest)
{
// we are the highest path so far so let's return it
yield return path;
highest = sum;
}
}
continue;
}
// where ever we are, lets look at all the valid x's in the next row
var start = Math.Max(0, x - 1);
var finish = Math.Min(matrix.GetLength(1) - 1, x + 1);
// add them to the queue
// we inefficiently create a new path, as list is a reference type and we don't want to reuse it
for (var newX = start; newX <= finish; newX++)
queue.Enqueue((newX, y + 1, new List<(int col, int value)>(path)));
}
}
Usage
// create a random matrix, make sure there the dimensions are going to produce a result
var y = _rand.Next(2, 5);
var matrix = new int[_rand.Next(y, y + 3), y];
// fill and print the matrix
Console.WriteLine("Matrix");
Console.WriteLine();
for (var i = 0; i < matrix.GetLength(0); i++)
{
for (var j = 0; j < matrix.GetLength(1); j++)
{
matrix[i, j] = _rand.Next(0, 20);
Console.Write(matrix[i, j].ToString().PadLeft(3));
}
Console.WriteLine();
}
Console.WriteLine();
Console.WriteLine("Best Path (column,Value)");
Console.WriteLine();
// get the best, which be the last
var best = FindPath(matrix).Last();
foreach (var item in best)
{
Console.WriteLine(item);
}
// show the result
Console.WriteLine();
Console.WriteLine("= " + best.Sum(x => x.value));
Output
Matrix
14 9 17 0
19 5 11 10
17 12 9 13
3 11 2 5
0 0 12 15
Best Path (column,Value)
(0, 14)
(0, 19)
(1, 12)
(2, 2)
(3, 15)
= 62
Full Demo Here
Additional Resources
Iterators
Tuple types (C# reference)
Array.GetLength(Int32) Method
Multidimensional Arrays (C# Programming Guide)
Random.Next Method
Queue Class
List.Contains(T) Method
Enumerable.Any Method
Enumerable.Sum Method
Enumerable.Last Method
Math.Min Method
Math.Max Method
continue (C# Reference)
Enumerable.Select Method
Enumerable.ToList methd

Related

Any suggestion on finding a standard tabulation way to solve this

I have this question:
Given a grid of positive numbers, start from 0,0 and end at n,n.
Move only to right and down.
Find path with sum of numbers is maximum.
Input Example:
5 1 2
6 7 8
1 4 9
output Example:
35
I solved it with many scenarios for example i wrote an easy equation to get the max path length then take some decisions to get the correct result.
But this example is easy to got that it should solved using dynamic programming as it's recursion and some overlaps with small space:
fn(0,0)
fn(0,1) f(1,0)
f(0,2) f(1,1) f(2,0) f(1,1)
I solved it easy using Memoization technique:
public int GetMaxPathTopDown(int r, int c, int[,] arr, int[,] mem)
{
if (r >= arr.GetLength(0) || c >= arr.GetLength(1))
{
return 0;
}
// Base Case
if (r == arr.GetLength(0) - 1 && c == arr.GetLength(1) - 1)
{
return arr[r, c];
}
if (mem[r, c] != -1)
{
return mem[r, c];
}
int firstPath = GetMaxPathTopDown(r, c + 1, arr, mem);
int secondPath = GetMaxPathTopDown(r + 1, c, arr, mem);
return mem[r, c] = arr[r, c] + Math.Max(firstPath, secondPath);
}
but I want to find standard solution to solve it using Tabulation way. I tried many solutions but i thought it's not a correct way of tabulation so any help?
#Marzouk, you can try this:
dp[i, j] will be the maximum sum you can get from (0, 0) to (i, j). Now, dp[i, j] will depend on dp[i - 1, j] and dp[i, j - 1]. So, the recurrence will be:
dp[i, j] = max(dp[i - 1, j], dp[i, j - 1]) + mt[i, j]
You need to handle corner cases here (i = 0 or j = 0).
using System;
class MainClass {
public static void Main (string[] args) {
int[,] mt = new[,] { {5, 1, 2}, {6, 7, 8}, {1, 4, 9} };
int[,] dp = new[,] { {0, 0, 0}, {0, 0, 0}, {0, 0, 0} };
int R = mt.GetLength(0);
int C = mt.GetLength(1);
for (int i = 0; i < R; i++)
for (int j = 0; j < C; j++) {
dp[i, j] = mt[i, j];
if (i > 0 && j > 0) {
dp[i, j] += Math.Max(dp[i - 1, j], dp[i, j - 1]);
} else if (i > 0) {
dp[i, j] += dp[i - 1, j];
} else if (j > 0) {
dp[i, j] += dp[i, j - 1];
}
}
Console.WriteLine(dp[R-1, C-1]);
}
}
The basic steps to build bottom-up dynamic programming solutions are as follows:
1- Determine the required set of parameters that uniquely describe the problem (the state).
2- If there are N parameters required to represent the states, prepare an N dimensional DP table, with one entry per state.
This is equivalent to the memo table in top-down DP. However, there are differences. In bottom-up DP, we only need to initialize some cells of the DP table with known initial values (the base cases). Recall that in topdown DP, we initialize the memo table completely with dummy values (usually -1) to indicate that we have not yet computed the values.
3- Now, with the base-case cells/states in the DP table already filled, determine the cells/states that can be filled next (the transitions).
Repeat this process until the DP table is complete.
For the bottom-up DP, this part is usually accomplished through iterations, using loops.
lets build a table for 3*3 array with values:
5 1 2
3 1 2
0 1 1
we notice that we need two parameters : Move_right, Move_down
initially, the first row and column should have the cumulative sum of the array elements to start maximization
then follow the steps in the attached picture which explain the code below
dp[0,0] = arr[0,0];
for (int i = 1; i < n; i++){
dp[0,i] = arr[0,i] + dp[0,i - 1];
}
for (int i = 1; i < n; i++){
dp[i,0] = arr[i,0] + dp[i-1,0];
}
for (int i = 1; i < n; i++){
for (int j = 1; j < n; j++){
dp[i,j] = arr[i,j] + Math.max(dp[i - 1,j], dp[i,j - 1]);
}
}
Console.WriteLine(dp[n - 1,n - 1]);
the required answer should be 12, and you find it in the most right-down cell
building table

How can I extract the places values of a number?

I'm working on a math game in the Unity game engine using C#, specifically a reusable component to teach the grid method for multiplication. For example, when given the numbers 34 and 13, it should generate a 3X3 grid (a header column and row for the multiplier and multiplicand place values and 2X2 for the number of places in the multiplier and multiplicand). Something that looks like this:
My issue is that I don't know the best way to extract the place values of the numbers (eg 34 -> 30 and 4). I was thinking of just converting it to a string, adding 0s to the higher place values based on its index, and converting it back to an int, but this seems like a bad solution. Is there a better way of doing this?
Note: I'll pretty much only be dealing with positive whole numbers, but the number of place values might vary.
Thanks to all who answered! Thought it might be helpful to post my Unity-specific solution that I constructed with all the replies:
List<int> GetPlaceValues(int num) {
List<int> placeValues = new List<int>();
while (num > 0) {
placeValues.Add(num % 10);
num /= 10;
}
for(int i = 0;i<placeValues.Count;i++) {
placeValues[i] *= (int)Mathf.Pow(10, i);
}
placeValues.Reverse();
return placeValues;
}
Take advantage of the way our number system works. Here's a basic example:
string test = "12034";
for (int i = 0; i < test.Length; ++i) {
int digit = test[test.Length - i - 1] - '0';
digit *= (int)Math.Pow(10, i);
Console.WriteLine("digit = " + digit);
}
Basically, it reads from the rightmost digit (assuming the input is an integer), and uses the convenient place value of the way our system works to calculate the meaning of the digit.
test.Length - i - 1 treats the rightmost as 0, and indexes positive to the left of there.
- '0' converts from the encoding value for '0' to an actual digit.
Play with the code
Perhaps you want something like this (ideone):
int n = 76302;
int mul = 1;
int cnt = 0;
int res[10];
while(n) {
res[cnt++] = (n % 10) * mul;
mul*=10;
cout << res[cnt-1] << " ";
n = n / 10;
}
output
2 0 300 6000 70000
My answer is incredibly crude, and could likely be improved by someone with better maths skills:
void Main()
{
GetMulGrid(34, 13).Dump();
}
int[,] GetMulGrid(int x, int y)
{
int[] GetPlaceValues(int num)
{
var numDigits = (int)Math.Floor(Math.Log10(num) + 1);
var digits = num.ToString().ToCharArray().Select(ch => Convert.ToInt32(ch.ToString())).ToArray();
var multiplied =
digits
.Select((d, i) =>
{
if (i != (numDigits - 1) && d == 0) d = 1;
return d * (int)Math.Pow(10, (numDigits - i) - 1);
})
.ToArray();
return multiplied;
}
var xComponents = GetPlaceValues(x);
var yComponents = GetPlaceValues(y);
var arr = new int[xComponents.Length + 1, yComponents.Length + 1];
for(var row = 0; row < yComponents.Length; row++)
{
for(var col = 0; col < xComponents.Length; col++)
{
arr[row + 1,col + 1] = xComponents[col] * yComponents[row];
if (row == 0)
{
arr[0, col + 1] = xComponents[col];
}
if (col == 0)
{
arr[row + 1, 0] = yComponents[row];
}
}
}
return arr;
}
For your example of 34 x 13 it produces:
And for 304 x 132 it produces:
It spits this out as an array, so how you consume and display the results will be up to you.
For two-digit numbers you can use modulo
int n = 34;
int x = n % 10; // 4
int y = n - x; // 30

Smallest cost traversal of an array

How do you compute the smallest cost traversal of an integer array using steps and jumps, while also counting the first and last element of the array? A step is moving to the next immediate value in the array e.g. array[currentIndex + 1], and a jump is moving two spots e.g. array[currentIndex + 2]. I have the following function which I want to return the minimum sum started, it adds the first and last elements to the sum, but I'm stuck on the middle values of the array.
An example of this would be {2, 10, 4, 14, 44, 28, 16, 18} -> 66
which would add indexes 0, 2, 3, 5, and 7.
====
public int Cost(int[] board)
{
int sum = board[0];
int index = 0;
while (index < board.Length)
{
//Add the final array value to the sum
if (index + 1 == board.length)
{
sum += board[index];
break;
}
//Add other values here
index++;
}
return sum;
}
You can try this:
public int Cost(int[] board)
{
int[] cost = new int[board.Length];
for (int i = 0; i < board.Length; i++) {
if (i == 0) {
cost[i] = board[0];
} else if (i == 1) {
cost[i] = board[1] + cost[0];
} else {
cost[i] = board[i] + Math.Min(cost[i - 1], cost[i - 2]);
}
}
return cost[board.Length - 1];
}
One possible solution:
public int Cost(int[] board, int step = 1)
{
if (board == null) return -1;
if (board.Length == 0) return 0;
// always add first and last index (if its not the first)
int sum = board[0];
if (board.Length > 1) sum += board[board.Length - 1];
// assumes step is > 0
for (int i = step; i < (board.Length - 1); i += step)
{
sum += board[i];
}
return sum;
}
This allows for step to be a parameter. Maybe now you want to step either 1 or 2 away from the start. Maybe later you want to step 5 spots away.

Maximum Submatrix Sum of nxn matrices

I'm trying to get the maximum Submatrix Sum from given nxn matrices. From what I read around the algorithm have a complexity of n^3 (kadane). I tried to implement it in java using a code I found here on stackoverflow (written by Anders Gustafsson in C# in these post) but it seems to not always work.
my code is this:
public static void maxSubMatrix(int matrix[][]) {
// O(n^3) Max Sum Submatrix
int row = matrix.length;
int col = matrix[0].length; // Get the col-length by taking the length of
// row 0 (in programm the matrices will be n x n)
// Initialise maxSum
int maxSum = 0;
// Set left column
for (int left=0; left<col; left++) {
// Initialise array
int tempArray[] = new int[row];
// Set right column by left column (outer loop)
for (int right=left; right<col; right++){
// Calculate sum between current left-right for every row 'i'
for (int i=0; i<row; i++)
tempArray[i] = matrix[i][right];
// ----
// Find maximum sum in tempArray[]
int sum = maxSubArray(tempArray);
// Compare sum with maxSum so far, and update it if necessary
if (sum > maxSum) maxSum = sum;
}
}
System.out.println(maxSum);
}
// ==========================
public static int maxSubArray(int array[]){
// Kadane O(n) Max Sum Subarray
int maxSum = 0;
int tempSum = 0;
for (int i=0; i<array.length; i++){
int b = array[i];
if (tempSum + b > 0) tempSum += b;
else tempSum = 0;
maxSum = Math.max(maxSum, tempSum);
}
return maxSum;
}
I have three examples:
Matrix 1
-2 -3
-1 -4
Output is 0 (Also the empty 0x0 Matrix is a solution)
Matrix 2
2 -1
-2 -1
Output is 2
Matrix 3
-1 3
3 -1
Output is 3, but should be 4
Maybe someone can see the error. I'm also open to completely new idea to implement it.
You have just forgotten to add the next rigth element to the temp-array:
(int i=0; i<row; i++)
tempArray[i] += matrix[i][right];
Now everything's fine! ;)
Greetz

find diagonal in 2 dimensional array

I have a 2-dimensional array with user-entered values. I need to find sum of the even elements in the diagonal of the array.
I know how to declare the array and get it filled by the user, but I'm not sure what even elements in the main diagonal really means.
I know I can find out if a number is even by saying:
if n / 2 == 0
Once I've reported the sum of the even elements in the diagonal, I would like to replace all 0 values in the array with ones.
Diagonal means all places where x and y cordinates are the same
Do if your array contains:
1 3 8 5
3 3 9 7
4 4 5 7
5 1 7 4
Then the diagonal are in bold.
Assuming the array is a square:
int sum = 0;
for(int i = 0; i < numOfArrayRows; i++)
{
//Use the mod operator to find if the value is even.
if(array[i][i] % 2 == 0)
sum += array[i][i];
//Change 0's to ones
for(int j = 0; j < numOfArrayCols; j++)
if(array[i][j] == 0)
array[i][j] = 1;
}
Also, next time add the "Homework" tag if you have a homework question :P
With a two-dimensional array it's really easy, since you don't need any index magic:
int a[N][N] = ...;
int sum = 0;
for(int i=0; i<N; ++i)
if(a[i][i] % 2 == 0) //or a[i] & 1, if you like, just check if it's dividable by 2
sum += a[i][i];
This C++ code shouldn't be that different in C or C#, but you should get the point. Likewise the second question would be as simple as:
int a[M][N] = ...;
for(i=0; i<M; ++i)
for(j=0; j<N; ++j)
if(a[i][j] == 0)
a[i][j] = 1;
And I suspec that the main diagonal is the one that begins with coordinates 0,0.
To replace 0 elements with 1 you would do something like:
if (array[i,j] == 0) array[i,j] == 1;
This sounds like homework - however I will help out :)
So if you have an 2D array, and in order to find the sum of the diagonal values, you will know that the indices of both of the values would match in order to provide you with each of the diagonal values.
To iterate through these you could use a simple loop that would sum up every diagonal value, as shown:
//Your Sum
int sum = 0;
//This will iterate and grab all of the diagonals
//You don't need to iterate through every element as you only need
//the diagonals.
for(int i = 0; i < sizeOfArray; i++)
{
//This will add the value of the first, second, ... diagonal value to your sum
sum += array[i,i];
}
To set each of the values that is 0 to 1, you could iterate through each element of the array and check if the value is 0, then set that value to 1, for example:
for(int i = 0; i < sizeOfArray; i++)
{
for(int j = 0; j < sizeOfArray; j++)
{
//Check if this value is 0;
//If it is 0, set it to 1, otherwise continue
}
}
int[,] array = new int[,] {{1,2,3},
{4,5,6},
{7,8,9}};
//Suppose you want to find 2,5,8
for(int row = 0; row < 3; row++)
{
for(int column = 0; column < 3; column++)
{
if((row == 0 && column == 1) || (row == 1 && column == 1) || (row == 2 && column == 1))
{
Console.WriteLine("Row: {0} Column: {1} Value: {2}",row + 1, column + 1, array[row, column]);
}
}
}
Here is the code you need, not much explain:
//Read the size of the array, you can get it from .Count() if you wish
int n = Convert.ToInt32(Console.ReadLine());
int[][] a = new int[n][];
//Reading all the values and preparing the array (a)
for (int a_i = 0; a_i < n; a_i++)
{
string[] a_temp = Console.ReadLine().Split(' ');
a[a_i] = Array.ConvertAll(a_temp, Int32.Parse);
}
//performing the operation (google what diagonal matrix means)
int PrimarySum = 0, SecondarySum = 0;
for (int i = 0; i < n; i++)
{
//The If condition is to skip the odd numbers
if (a[i][i] % 2 == 0) { PrimarySum += a[i][i]; }
//For the reverse order
int lastelement = a[i].Count() - 1 - i;
if (a[i][lastelement] % 2 == 0) { SecondarySum += a[i][lastelement]; }
}
//Get the absolute value
Console.WriteLine(Math.Abs(PrimarySum - SecondarySum).ToString());
Console.ReadKey();

Categories