Reconstructing the path that gives the maximum path sum in a matrix - java

I'm trying to find a maximum path sum in the matrix. The starting position must be in a[0][0] (top-left) and the ending position must be in a[n][m] (bottom-right). The move is only allowed to the right, down, or diagonal.
Here is my solution:
public class Main {
public static int maxa(int[][] a) {
int m = a.length, n = a[0].length;
int[][] dp = new int[m][n];
dp[0][0] = a[0][0];
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + a[i][0];
}
for (int j = 1; j < n; j++) {
dp[0][j] = dp[0][j - 1] + a[0][j];
}
System.out.println("Route Path: ");
System.out.print(a[0][0] + " ");
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = findMax(dp[i - 1][j], dp[i][j - 1], dp[i-1][j-1]) + a[i][j];
if (a[i - 1][j] > a[i][j - 1]) {
System.out.print(a[i - 1][j] + " ");
} else {
System.out.print(a[i][j - 1] + " ");
}
}
}
System.out.print(a[m - 1][n - 1] + " ");
System.out.println();
System.out.println("Result: ");
return dp[m - 1][n - 1];
}
public static int findMax(int num1, int num2, int num3) {
if (num1 >= num2 && num1 >= num3)
return num1;
else if (num2 >= num1 && num2 >= num3)
return num2;
else
return num3;
}
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int MAX = 30;
int MIN = -30;
Random random = new Random();
System.out.print("Enter values of Rows: ");
int n = input.nextInt();
System.out.print("Enter values of Columns: ");
int m = input.nextInt();
int[][] myMatrix = new int[n][m];
for (int row = 0; row < n; row++) {
for (int col = 0; col < m; col++) {
// myMatrix[row][col] = random.nextInt(MAX - MIN) + MIN;
myMatrix[row][col] = input.nextInt();
}
}
System.out.println();
System.out.println("Your Random Matrix: ");
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
System.out.print(myMatrix[i][j] + "\t");
}
System.out.println();
}
System.out.println();
System.out.println(maxa(myMatrix));
}
}
The result of the sum is correct but my code gives the wrong route path.
For example, if I have a 3x3 matrix = [{1,2,3},{4,-5,6},{7-8,9}], then the result sum I get is 22 which is correct, but the route path I get is 1 4 3 -5 8 9 which is wrong. I expected my route path output to be 1 4 8 9.
What can I do to fix the problem and produce the correct path?

I'm not sure I follow your expected output, but the inline prints are generally not a great approach to the problem. Generally, you want to compute it without a side effect and return it so the caller can use the result programmatically, optionally to print.
In any case, your printing condition if (a[i - 1][j] > a[i][j - 1]) isn't enough to differentiate which of 3 possible moves was chosen on the best path. We need to find the largest of 3 subproblems in the DP table, not the input matrix -- looking at a is meaningless because it's only a local maxima at best.
In the case of a tie, it doesn't matter which move we pick from the DP table, all tied subproblems have equally maximal path scores.
While it's possible to figure out which number was taken along the way up by making that 3-way comparison, the typical(?) approach I'm familiar with for DP is to backtrack from dp[m-1][n-1] to dp[0][0] and reconstruct the path based on the DP table. It's free lunch from a time complexity standpoint and lets you separate the logic into a distinct function.
The logic for rebuilding the path is:
Start at i = m - 1, j = n - 1 (the bottom-right corner).
Repeat until i == 0 and j == 0 (the top-left corner):
Add a[i][j] to the path.
If i > 0 and j > 0, find the best of dp[i-1][j], dp[i-1][j-1], dp[i][j-1]. If the best was [i-1][j-1], decrement both i and j by 1 -- we took a diagonal move; otherwise decrement either i or j depending on which move was better.
Otherwise, i == 0 or j == 0 and the path is on a top or left edge and we'll just decrement i or j until we get to the goal.
Add a[0][0] to the path, reverse it and return it.
Here's a proof of concept:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class Main {
public static int maxPathSum(int[][] a) {
final var table = makeDPTable(a);
if (table.length > 0 && table[0].length > 0) {
return table[table.length-1][table[0].length-1];
}
return 0;
}
public static ArrayList<Integer> maxPath(int[][] a) {
return reconstructPath(makeDPTable(a), a);
}
private static ArrayList<Integer> reconstructPath(int dp[][], int a[][]) {
final int m = a.length;
if (m == 0) {
return new ArrayList<Integer>();
}
final int n = a[0].length;
final var path = new ArrayList<Integer>();
for (int i = m - 1, j = n - 1; i > 0 || j > 0;) {
path.add(a[i][j]);
if (i > 0 && j > 0) {
final int bestIdx = indexOfMax(
dp[i-1][j-1],
dp[i][j-1],
dp[i-1][j]
);
switch (bestIdx) {
case 0: i--;
case 1: j--; break;
case 2: i--; break;
}
}
else if (i > 0) {
i--;
}
else {
j--;
}
}
path.add(a[0][0]);
Collections.reverse(path);
return path;
}
private static int[][] makeDPTable(int[][] a) {
final int m = a.length;
if (m == 0) {
return new int[0][0];
}
final int n = a[0].length;
final var dp = new int[m][n];
dp[0][0] = a[0][0];
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i-1][0] + a[i][0];
}
for (int j = 1; j < n; j++) {
dp[0][j] = dp[0][j-1] + a[0][j];
}
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = a[i][j] + max(
dp[i-1][j],
dp[i][j-1],
dp[i-1][j-1]
);
}
}
return dp;
}
private static int max(int ...nums) {
if (nums.length == 0) {
throw new IllegalArgumentException("nums cannot be empty");
}
int largest = nums[0];
for (int num : nums) {
largest = Math.max(num, largest);
}
return largest;
}
private static int indexOfMax(int ...nums) {
if (nums.length == 0) {
return -1;
}
int largest = nums[0];
int idx = 0;
for (int i = 1; i < nums.length; i++) {
if (nums[i] > largest) {
largest = nums[i];
idx = i;
}
}
return idx;
}
private static void print(int[][] m) {
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[0].length; j++) {
System.out.print(StringUtils.padLeft(m[i][j], 3));
}
System.out.println();
}
}
public static void main(String[] args) {
var tests = new TestCase[] {
new TestCase(
new int[][] {
{1, 2, 3},
{4,-5, 6},
{7,-8, 9},
},
new ArrayList<Integer>(Arrays.asList(1, 2, 3, 6, 9)),
21
),
new TestCase(
new int[][] {
{1, 2, 3},
{4,-5, 6},
{7, 8, 9},
},
new ArrayList<Integer>(Arrays.asList(1, 4, 7, 8, 9)),
29
),
new TestCase(
new int[][] {
{1, 2, 3, 20},
{4,-5, 6, -5},
{7, 8, 9, 10},
},
new ArrayList<Integer>(Arrays.asList(1, 4, 7, 8, 9, 10)),
39
),
new TestCase(
new int[][] {
{1, 2, 3, -2},
{4,-5, -1, 20},
{0, 0, 9, 10},
},
new ArrayList<Integer>(Arrays.asList(1, 2, 3, 20, 10)),
36
),
new TestCase(
new int[][] {
{-1, -2, -3, -2},
{-4, -5, -1, -2},
},
new ArrayList<Integer>(Arrays.asList(-1, -2, -1, -2)),
-6
),
new TestCase(new int[][] {}, new ArrayList<Integer>(), 0),
};
for (var testCase : tests) {
if (maxPathSum(testCase.m) != testCase.expectedSum) {
print(testCase.m);
System.out.println("got sum: " + maxPathSum(testCase.m));
System.out.println("expected: " + testCase.expectedSum + "\n");
}
if (!maxPath(testCase.m).equals(testCase.expectedPath)) {
print(testCase.m);
System.out.println("got path: " + maxPath(testCase.m));
System.out.println("expected: " + testCase.expectedPath + "\n");
}
}
}
}
class TestCase {
public final int[][] m;
public final ArrayList<Integer> expectedPath;
public final int expectedSum;
public TestCase(
int[][] m,
ArrayList<Integer> expectedPath,
int expectedSum
) {
this.m = m;
this.expectedPath = expectedPath;
this.expectedSum = expectedSum;
}
}
class StringUtils {
public static <T> String padLeft(T t, int n) {
return String.format("%" + n + "s", t);
}
}

Related

Finding most frequency number in array

By using following I am able to find most frequently occurring integer in an array. But following code it will not work for few scenarios. How can I fix the code inside for loop? I want to enhance this approach only.
class FindingMostFrequencyOccur {
public static void main(String args[]) {
int A[] = { 1, 2, 3, 3, 1, 3, 1};
int M = 3; // Maximum Number in Array
int result = findFrequency(M, A);
System.out.println("Result "+result);
}
static int findFrequency(int M, int[] A) {
int N = A.length;
int[] count = new int[M + 1];
for (int i = 0; i <= M; i++)
count[i] = 0;
int maxOccurence = 1;
int index = -1;
for (int i = 0; i < N; i++) {
if (count[A[i]] > 0) {
int tmp = count[A[i]];
if (tmp > maxOccurence) {
maxOccurence = tmp;
index = i;
}
count[A[i]] = tmp + 1;
} else {
count[A[i]] = 1;
}
}
return A[index];
}
}
Can you we improve in this line
if (count[A[i]] > 0) {
int tmp = count[A[i]];
if (tmp > maxOccurence) {
maxOccurence = tmp;
index = i;
}
count[A[i]] = tmp + 1;
} else {
count[A[i]] = 1;
}
To get the frequency of m in a use:
static int findFrequency(int m, int[] a) {
return (int)IntStream.of(a).filter(i-> i==m).count();
}
To get a map of all frequencies in a use:
static Map<Integer, Integer> findFrequency(int[] a) {
Map<Integer, Integer> m = new HashMap<>();
IntStream.of(a).distinct().forEach(i->{
m.put(i, findFrequency(i,a));
});
return m;
}
In this logic, you take each element of the array, and find the number of times it is repeated to the right of it, in the array. This is given by local_frequency. If this happens to be more than max_frequency, then this number at arr[i] is stored in number, and then max_frequency stores local_frequency
public static void main(String[] args)
{
int[] arr = {1, 2, 3, 4, 3, 2, 1, 5, 5, 5, 4, 4, 3, 4};
int result = findMostFrequent(arr);
System.out.println(result + " is the most frequent number");
}
public static int findMostFrequent(int[] arr)
{
int number = arr[0];
int maxFrequency = 0;
for(int i =0 ; i < arr.length; i++)
{
int local_frequency = 0;
for(int j = i; j < arr.length; j++)
{
if(arr[i] == arr[j])
local_frequency++;
}
if(local_frequency > maxFrequency)
{
number = arr[i];
maxFrequency = local_frequency;
}
}
return number;
}

Using recursion, segregate an array of integers to have the even integers come before the odd

I need to use recursion that takes in array of integers and has the even integers appear before the odd integers i.e an array of [1,2,3,4,5] should look like [2,4,1,3,5]. Please note that the integers do not need to be in any specific order just even before odd. Here is what I have so far, any help would be appreciated!
public class EBOMain {
static int[] Array = { 1, 2, 3, 4, 5, 6};
static int n;
public static void main(String[] args) {
System.out.print("Array before sort: " );
for (int i = 0; i < Array.length; i++)
System.out.print(Array[i] +" ");
n = Array.length;
rearrange(Array, n);
System.out.print("\nArray after sort: " );
for (int i = 0; i < Array.length; i++)
System.out.print(Array[i] +" ");
}
public static void rearrange(int []Array,int n){
if (n==0)
return;
else if(Array[n-1]%2==0) {
for(int i=0;i<n-1;i++) {
if(Array[i]%2!=0) {
int temp = Array[i];
Array[i]= Array[n-1];
Array[n-1] = temp;
rearrange(Array,n-1);
}
}
}
else
rearrange(Array,n-1);
}
}
You forgot to initialize n with the length of the array in the first call to rearrange. See this corrected code. Make sure you use Java code conventions; I renamed the Arrayvariable to array.
public class EBOMain {
static int[] array = { 1, 2, 3, 4, 5, 6 };
public static void main(String[] args) {
System.out.print("array before sort: ");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
rearrange(array, array.length); // <-- Change is here
System.out.print("\narray after sort: ");
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
public static void rearrange(int[] array, int n) {
if (n == 0) {
return;
} else if (array[n - 1] % 2 == 0) {
for (int i = 0; i < n - 1; i++) {
if (array[i] % 2 != 0) {
int temp = array[i];
array[i] = array[n - 1];
array[n - 1] = temp;
rearrange(array, n - 1);
}
}
} else {
rearrange(array, n - 1);
}
}
}
Output:
array before sort: 1 2 3 4 5 6
array after sort: 6 2 4 1 3 5

merging 3 sorted arrays

public class array12 {
static void merge_sort(int A[], int start, int end) {
if (end - start > 1) {
int middle1 = (2 * start + end + 1) / 3 - 1;
int middle2 = 2 * middle1 - start + 1;
merge_sort(A, start, middle1);
merge_sort(A, middle1 + 1, middle2);
merge_sort(A, middle2 + 1, end);
merge(A, start, middle1, middle2, end);
}
}
static void merge(int[] x, int start, int middle1, int middle2, int end) {
int n1 = middle1 - start + 1;
int n2 = middle2 - middle1;
int n3 = end - middle2;
int left[] = new int[n1]; // defining and initialising three arrays .
int mid[] = new int[n2];
int right[] = new int[n3];
for (int i = 0; i < left.length; i++) {
left[i] = x[i + start];
}
for (int i = 0; i < mid.length; i++) {
mid[i] = x[i + middle1 + 1];
}
for (int i = 0; i < right.length; i++) {
right[i] = x[i + middle2 + 1];
}
int i = 0;
int j = 0;
int k = 0;
int c = start;
// finding minimum element from the three arrays .
while (i < n1 && j < n2 && k < n3) {
if (left[i] <= mid[j] && left[i] <= right[k]) {
x[c] = left[i];
i++;
c++;
} else if (mid[j] <= left[i] && mid[j] <= right[k]) {
x[c] = mid[j];
j++;
c++;
} else {
x[c] = right[k];
k++;
c++;
}
}
// now only two arrays are left to be compared
while (i < n1 && j < n2) {
if (left[i] <= mid[j]) {
x[c] = left[i];
i++;
c++;
} else {
x[c] = mid[j];
j++;
c++;
}
}
while (j < n2 && k < n3) {
if (mid[j] <= right[k]) {
x[c] = mid[j];
j++;
c++;
} else {
x[c] = right[k];
k++;
c++;
}
}
while (i < n1 && k < n3) {
if (left[i] <= right[k]) {
x[c] = left[i];
i++;
c++;
} else {
x[c] = right[k];
k++;
c++;
}
}
// now only single array is left out of left[] , mid[] and right[].
while (i < n1) {
x[c] = left[i];
i++;
c++;
}
while (j < n2) {
x[c] = mid[j];
j++;
c++;
}
while (k < n3) {
x[c] = right[k];
k++;
c++;
}
System.out.println("");
// printing array elements after every merge operation .
for (int e = 0; e < x.length; e++) {
System.out.print(x[e] + " ");
}
}
public static void main(String[] args) {
int[] x = new int[9];
for (int i = 0; i < x.length; i++) {
x[i] = x.length - i;
}
System.out.println("initial array is : ");
for (int i = 0; i < x.length; i++) {
System.out.print(x[i] + " ");
}
System.out.println("");
merge_sort(x, 0, x.length - 1);
System.out.println("");
System.out.println("");
System.out.println(" sorted array is : ");
for (int i = 0; i < x.length; i++) {
System.out.print(x[i] + " ");
}
}
}
I am trying to merge 3 sorted arrays . I have been able to develop code for array size equal to power of 3 . I am unable to implement it with some other array size . I have tried to change values of middle1 and middle2 but am experiencing serious trouble . Setting their values is the main concern . Merging step is quite simple and is not causing problems .
What changes are required in my code so that it may work for any array size ? Can it be implemented using this approach ? I dont want size of any of the three arrays , left[] , mid[] and right[] to be zero at any time .
Please help .
Here's a similar answer to YCF_L's, but simplified (still uses Java 8):
public static int[] sortMultipleArrays(int[]... arrays) {
return Arrays.stream(arrays)
.flatMapToInt(Arrays::stream)
.sorted()
.toArray();
}
Output:
[1, 2, 3, 5, 6, 7, 9, 10, 12, 13, 17, 20, 21, 24]
I don't follow your merge code. It seems overly complicated.
Here is a method for merging an unlimited number of sorted arrays, each a varying size.
private static int[] mergeSortedArrays(int[]... arrays) {
int totalLen = 0;
for (int[] arr : arrays)
totalLen += arr.length;
int[] idx = new int[arrays.length];
int[] merged = new int[totalLen];
for (int i = 0; i < totalLen; i++) {
int min = 0, minJ = -1;
for (int j = 0; j < arrays.length; j++)
if (idx[j] < arrays[j].length)
if (minJ == -1 || min > arrays[j][idx[j]]) {
min = arrays[j][idx[j]];
minJ = j;
}
merged[i] = min;
idx[minJ]++;
}
return merged;
}
Test
int[] a = { 3, 5, 9, 13, 17, 21 };
int[] b = { 2, 10, 20 };
int[] c = { 1, 7, 12, 24 };
int[] d = { 6 };
int[] merged = mergeSortedArrays(a, b, c, d);
System.out.println(Arrays.toString(merged));
Output
[1, 2, 3, 5, 6, 7, 9, 10, 12, 13, 17, 20, 21, 24]
If using class "Integer" instead of primitive int is not a problem you can use this, basically first do the merge and after sort them: you can do the call Arrays.sort even in the same method and call it mergeAndSort, if you want...
import java.util.Arrays;
public class Main {
public static Integer[] merge(Integer[]... arrays) {
int count = 0;
for (Integer[] array : arrays) {
count += array.length;
}
Integer[] mergedArray = (Integer[]) java.lang.reflect.Array.newInstance(arrays[0][0].getClass(), count);
int start = 0;
for (Integer[] array : arrays) {
System.arraycopy(array, 0, mergedArray, start, array.length);
start += array.length;
}
return mergedArray;
}
public static void main(String[] args) {
Integer[] array1 = {3, 5, 6, 7, 78, 100};
Integer[] array2 = {5, 6, 7, 8, 9};
Integer[] array3 = {2, 6, 7};
Integer[] merged1 = merge(array1, array2);
Arrays.sort(merged1);
Integer[] merged2 = merge(array1, array2, array3);
Arrays.sort(merged2);
printArray(merged1);
printArray(merged2);
}
public static void printArray(Integer[] x) {
System.out.println("--ToString--");
for (Integer var : x) {
System.out.println(var);
}
System.out.println("----");
}
}

Print elements of a matrix in diagonal stripes

I want to generate a matrix with consecutive numbers starting from 1, in this form
zig zag matrix
public static int[][] Zig_Zag(final int size) {
int[][] data = new int[size][size];
int i = 1;
int j = 1;
for (int element = 0; element < size * size; element++) {
data[i - 1][j - 1] = element;
if ((i + j) % 2 == 0) { // Even stripes if (j < size) j++; else i+=
// 2; if (i > 1) i--; } else { // Odd
// stripes if (i < size) i++; else j+= 2; if
// (j > 1) j--; } } return data; }
}
}
return data;
}
Can anybody help?
Try this
public static int[][] Zig_Zag(int size) {
int[][] a = new int[size][size];
int n = 1;
for (int r = size, c = 0; r >= 0; --r)
for (int i = r, j = c; i < size; ++i, ++j)
a[i][j] = n++;
for (int r = 0, c = 1; c < size; ++c)
for (int i = r, j = c; j < size; ++i, ++j)
a[i][j] = n++;
return a;
}
and
int[][] a = Zig_Zag(4);
for (int[] r : a)
System.out.println(Arrays.toString(r));
result:
[7, 11, 14, 16]
[4, 8, 12, 15]
[2, 5, 9, 13]
[1, 3, 6, 10]
Try this code :
public static int[][] Zig_Zag(final int size) {
int[][] data = new int[size][size];
int i = 1;
int j = 1;
for (int element = 1; element <= size * size; element++) {
data[i - 1][j - 1] = element;
if ((i + j) % 2 == 0) {
// Even stripes
if (j < size)
j++;
else
i += 2;
if (i > 1)
i--;
} else {
// Odd stripes
if (i < size)
i++;
else
j += 2;
if (j > 1)
j--;
}
}
return data;
}
public static void main(String[] args) {
int[][] data = Zig_Zag(4);
for(int i=0; i<data.length;i++){
for(int j=0; j<data[i].length;j++){
System.out.print(data[i][j]+" ");
}
System.out.println("");
}
}
Output:
1 2 6 7
3 5 8 13
4 9 12 14
10 11 15 16
Not a very elegant solution:
private static int triangle_below(int n) {
return n * (n + 1) / 2;
}
private static int except_triangle_above(int size, int n) {
return size * size - triangle_below(2 * size - n);
}
private static int[][] gen(int size) {
int[][] m = new int[size][size];
for (int i = 0; i < size; ++i) {
for (int j = 0; j < size; ++j) {
// already filled cells in lower diagonal layers
int k = Math.min(
triangle_below(i + j),
except_triangle_above(size, Math.max(size, i + j + 1))
);
// position in current layer
int l = Math.min(j + 1, size - i);
m[size - i - 1][j] = k + l;
}
}
return m;
}

add up array element 3 by 3

I am currently learning java arrays. Is there any way I can loop through an array of size 9
and do the following
sum1 = array[0] + array[1] + array[2]
sum2 = array[3] + array[4] + array[5]
sum3 = array[6] + array[7] + array[8]
I tried but I could not achieve my desired output.
public class Sum {
public static void main(String[] args) {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int sum = 0;
for (int i = 0; i < array.length; i++) {
// if it's divisible by 3, reset the sum to 0
if (i > 1 && i % 3 == 0) {
sum = 0;
}
else {
sum += array[i];
System.out.println(sum);
}
}
}
}
you can use
for (int i = 0; i < array.length; i++) {
sum += array[i];
if (i > 0 && (i + 1) % 3 == 0) {
System.out.println(sum);
sum = 0;
}
}
You're resetting the sum to 0 ok, but then not adding the number that you've reset it on (because of your else condition.)
Use for loop by increasing by 3, knowing that the length of the array is a multiple of 3.
public class Sum {
public static void main(String[] args) {
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int sum = 0;
for (int i = 0; i < array.length; i+=3) { // increase i by 3
sum += array[i] + array[i+1] + array[i+2];
System.out.println(sum);
sum=0;
}
}
}

Categories