I want to save a triangular matrix in a 1 dim array (to minimize needed space, all zeros are left out) and create a function get() to find a specific entry from the original matrix.
For example:
Lets look at the following triangular matrix :
0 1 2 3
0 0 4 5
0 0 0 6
0 0 0 0
I am saving this matrix like this:
double[] test = {1,2,3,4,5,6};
So all the zeros are left out.
I want to write a function that gives me a value of the original matrix:
get(3,4)
should give me 6
I am checking the input to see if its out of bound and if it is below or on the diagonal.
//Checking if input is valid
if (i <= n && j <= n && i >= 1 && j >= 1){
if( j <= i ){
return 0.0;
}else {
}
}
This works.
How do I proceed though? I have trouble finding the equivalent matrix entry in my array.
Any help would be appreciated.
EDIT:
My whole code:
public class dreiecksmatrix {
int n = 4;
double[] a = {1,2,3,4,5,6};
public double get( int i, int j){
//Checking if input is valid
if (i <= n && j <= n && i >= 0 && j >= 0){
if( j <= i ){
return 0.0;
}else {
}
}
return 1.0;
}
public static void main(String [] args ){
dreiecksmatrix test = new dreiecksmatrix();
System.out.println(test.get(2,3));
}
}
Here is the sample code calculating the value of top-triange. No corner cases check like i,j >= 1 yet, but it's easy to add them.
arr = [[0, 1, 2, 3, 4],
[0, 0, 5, 6, 7],
[0, 0, 0, 8, 9],
[0, 0, 0, 0, 10],
[0, 0, 0, 0, 0]];
flatArr = [1,2,3,4,5,6,7,8,9,10];
n = 5; // matrix size
i = 1;
j = 3;
if (j <= i) {
alert(0);
} else {
pos = 0;
// find an offset caused by first (i - 1) lines
for (k = 1; k < i; k++) {
pos += n - k;
}
// find an offset in line x
pos += j - i;
// array index start from 0 so decrement value
pos = pos - 1;
alert('flatArr[' + pos + '] = ' + flatArr[pos]);
}
If you were instead to store the matrix by columns, there is a simple formula for the index into test of the i,j'th matrix element.
In your example you would have
double[] test = {1,2,4,3,5,6};
If Col(i) is the index pf the start of column i
then
Col(2) = 0
Col(3) = Col(2) + 1
..
Col(n) = Col(n-1) + n-1
Hence
Col(j) = ((j-1)*(j-2))/2
The i,j matrix element is stored i further on from the start of column j,
ie at Col(j)+i, so that you should add
return test[ ((j-1)*(j-2))/2 + i];
to your code
There is an analogous formula if you must store by rows rather than columns. It's a wee bit messier. The idea is to first figure out, starting with the last non-zero row, where the ends of the rows are solved.
Related
I need to take a 2D array and move everything as far left as possible. It is a 4x4 array and I have tried to do it but either only move certain items or the index goes out of bounds.
The gameBoard array looks like this:
{0 2 4 2}
{0 0 2 0}
{2 2 0 0}
{0 4 0 2}
and after you call the swipeLeft() method it should look like this:
{2 4 2 0}
{2 0 0 0}
{2 2 0 0}
{4 2 0 0}
There is also the issue of placing a zero into the previous index that you moved it from.
I created a double for loop to just loop through the array and tried to code something that would move it over but it hasn't worked.
Here was the code I had so far
public void swipeLeft() {
for ( int r = 0; r < gameBoard.length; r++ ) {
for ( int c = 0; c < gameBoard[r].length; c++ ) {
gameBoard[r][c] = gameBoard[r][ (c+1) %
gameBoard.length];
}
}
}
Based on your desired OUTPUT, it looks like swipeLeft() is supposed to push all non-zero values to the very left of their row, displacing the zeroes to the right of all non-zero values.
If that's correct, this is similar to Old Dog Programmer's approach, except all shifting is done "in place" without creating any new arrays:
import java.util.*;
class Main {
private static int[][] gameBoard;
public static void main(String[] args) {
gameBoard = new int[][] {
{0, 2, 4, 2},
{0, 0, 2, 0},
{2, 2, 0, 0},
{0, 4, 0, 2}
};
System.out.println("Before:");
displayBoard();
swipeLeft();
System.out.println("\nAfter:");
displayBoard();
}
public static void displayBoard() {
for(int[] row : gameBoard) {
System.out.println(Arrays.toString(row));
}
}
public static void swipeLeft() {
for(int[] row : gameBoard) {
// find the first blank (zero) spot
int nextIndex = 0;
while(nextIndex < row.length && row[nextIndex] != 0) {
nextIndex++;
}
// start with the first blank, and shift any non-zero
// values afterwards to the left
for(int col=nextIndex; col < row.length; col++) {
if (row[col] != 0) {
row[nextIndex] = row[col];
row[col] = 0;
nextIndex++;
}
}
}
}
}
Output:
Before:
[0, 2, 4, 2]
[0, 0, 2, 0]
[2, 2, 0, 0]
[0, 4, 0, 2]
After:
[2, 4, 2, 0]
[2, 0, 0, 0]
[2, 2, 0, 0]
[4, 2, 0, 0]
From the example in the question, it appears to me that what is wanted is to shift all non-zero elements to the left, and zero elements are shifted to the right. The order of the non-zero elements is to be retained.
Note that each row is independent of other rows.
One way to approach this is to create a method that works on a 1D array. This method takes a 1D array as a parameter, and returns another 1D array with the elements shifted:
public static int [] zeroShift (int [] arr) {
int [] left = new int [arr.length];
int count = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] != 0) {
left [count++] = arr [i];
}
}
return left;
}
This copies each non-zero element to a new array of the same size, keeping track (count) of how many have been copied so far. Note this relies on left being initialized to all-zeros.
Once that method is working, it can be used for gameBoard on a row-by-row basis:
public void swipeLeft() {
for (int r = 0; r < gameBoard.length; r++) {
gameBoard [r] = zeroShift (gameBoard [r]);
}
// output for testing
for (int i = 0; i < gameBoard.length; ++i) {
System.out.println(Arrays.toString(gameBoard[i]));
}
}
To rotate the array in place, you should roteate the array 3 times:
123456 -> 654312
654321
3456..
....12
public static void shiftLeft(int[] arr, int offs) {
if (offs <= 0)
return;
offs = arr.length - offs % arr.length - 1;
for (int i = 0, j = arr.length - 1; i < j; i++, j--)
swap(arr, i, j);
for (int i = 0, j = offs; i < j; i++, j--)
swap(arr, i, j);
for (int i = offs + 1, j = arr.length - 1; i < j; i++, j--)
swap(arr, i, j);
}
private static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
So your code intends to rotate the board one column to the left. Rotate? Well, the numbers you push out on the left might come back on the end, right?
Probably the line
gameBoard[r][c] = gameBoard[r][ (c+1) % gameBoard.length];
should be
gameBoard[r][c] = gameBoard[r][ (c+1) % gameBoard[r].length];
But try to do this stuff with pen & paper, and you should notice that you are going to loose one column/copy the values from the second column into the first, then copy that into the last column again.
You will need to change two items:
store the value from the first column somewhere if you still need it so you can push it into the last one.
only rotate the column data if it needs to be rotated. Or in other words, rotate the remainder of the row if you find a zero. In this case you do not need to remember the first column, as you will overwrite a zero and push a zero into the last column. And then it would not be called rotate but shift.
Exercise this with pen & paper until you can write down instructions for someone else to perform the same operation. Then you are ready to also write it in Java.
I was trying one problem to count the number of triplets in an array whose sum is less than target value.
Input: [-1, 4, 2, 1, 3], target=5
Output: 4
Explanation: There are four triplets whose sum is less than the target:
[-1, 1, 4], [-1, 1, 3], [-1, 1, 2], [-1, 2, 3]
My Code
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target - arr[i] - arr[left] - arr[right];
if (targetDiff > 0)
{
count++;
right--;
}
else
{
left++;
}
}
}
// TODO: Write your code here
return count;
}
}
It produces the output of 3 where as correct value should be 4 as per the above given input. My logic was , say , x + y + z < targetSum , it implies (targetSum - (x + y + z) ) > 0. If this is true I will increase the count and then decrement the right pointer , since array is sorted. If its not true then I will increment the left pointer . But my logic does not cover the triplet {-1, 2, 3}.
Below is the correct code given by author.
import java.util.*;
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for (int i = 0; i < arr.length - 2; i++) {
count += searchPair(arr, target - arr[i], i);
}
return count;
}
private static int searchPair(int[] arr, int targetSum, int first) {
int count = 0;
int left = first + 1, right = arr.length - 1;
while (left < right) {
if (arr[left] + arr[right] < targetSum) {
count += right - left;
left++;
} else {
right--; // we need a pair with a smaller sum
}
}
return count;
}
public static void main(String[] args) {
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 0, 2, 3 }, 3));
System.out.println(TripletWithSmallerSum.searchTriplets(new int[] { -1, 4, 2, 1, 3 }, 5));
}
}
The author has used the concept , say x + y + z < targetSum , it implies x + y < targetSum - z . But I don't get the logic of line count += right - left; . How author use this one line to capture the count. If some one can give me the intution on how to reach this inference. Also what is wrong with my code and what can I do to correct it.
A first issue with your code is that :
you only decrease the right index if the sum is inferior to the target.
However, since you have ordered your list, well you will only be entering that case until left=right.
Quick example : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], target=14
if 1+2+10 <13:
then you will only decrease 10 until you reach 2 in your array
and then you proceed to iterate to the next i-index, here going from 0 to 1.
Meaning that: you will never get the solutions in between such as [1,3,9] and all the one that follows.
I hope it helps you see where there was an error in the logic, which was not from the statement : (targetSum - (x + y + z) ) > 0 but from the action you take according to the result (True/False).
Now, I am not sure there would be an easy way to adapt your code corrctly, because the main issue here is that you have iterate over 2 indexes at once (right and left).
Now regarding your author's answer :
The trick behind :
count += right - left;
goes back to the issue you had, if i tame my example, for
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
it is basically saying that, since the array is ordered, if the sum of two integers with the right one is inferior to target, then it will also be true for all integers inferior to right :
1+2+10<14 => 1+2+9<13
And this statement is true for all integers between left and right, so instead of doing a loop for which we already have the answer, he adds to count the differences between right and left, in other words, the number of integers in your array that will be greater than left and lower than right.
Now that i have explained that, you could use the same "trick" to your code:
class TripletWithSmallerSum {
public static int searchTriplets(int[] arr, int target) {
Arrays.sort(arr);
int count = 0;
for(int i = 0; i < arr.length - 2; i++)
{
int left = i + 1;
int right = arr.length - 1;
while(left < right)
{
int targetDiff = target -( arr[i] + arr[left] + arr[right]);
if (targetDiff > 0)
{
count += right - left;
left++;
}
else
{
right--;
}
}
}
// TODO: Write your code here
return count;
}
}
I tried to be as detailed as possible, hope it helps you understand better!
For example I have array with length n=3:
for(int i = 0; i < n; i++) {
array[i] = i;
}
So the cases should be:
1. 0
2. 1
3. 2
4. 0 1
5. 0 2
6. 1 2
7. 0 1 2
So the number of cases should be 7 for n = 3.
In my code:
int n = 3;
int[] array = new int[n];
for (int i = 0; i < n; i++) {
array[i] = i;
}
int sum = 0;
for (int i = 0; i < n; i++) {
System.out.println(array[i] + " ");
sum++;
for (int j = i; j < n; j++) {
System.out.print(array[j] + " ");
}
System.out.println();
sum++;
}
System.out.println("sum = " + sum);
Output is:
0
0 1 2
1
1 2
2
2
sum = 6
The number 2 is two times so it is wrong and sum is actually = 5. And I don't get cases
4. 0 1
and
5. 0 2
How to count all possible cases?
Sets, not arrays
The first important observance is that you are not using fixed length arrays here but sets of different lengths.
Take a look at your example. You allow
0
1
2
0, 1
0, 2
1, 2
which are not all of size 3.
Also you don't differentiate between
0, 1
1, 0
so order doesn't matter, like in sets.
Power set
That's why you're actually describing power sets here. For the example set {0, 1, 2} its power set is defined as
P({0, 1, 2}) = {
{}, // empty set
{0},
{1},
{2},
{0, 1},
{0, 2},
{1, 2},
{0, 1, 2}
}
Fortunately there exists an easy closed formula for their size. If n is the size of the input set the size of the power set is
2^n
But they also count the empty set, so you will need to -1 if you don't want that:
2^n - 1
Solution
Thus in Java you could write
int Set<Integer> input = ...
int size = (int) Math.pow(2, input.size()) - 1;
and that's all, you don't need to build the contents manually.
But if you're curious and want to build them, take a look at questions like Obtaining a powerset of a set in Java. It's an implementation of the recursive formula shown at Wikipedia.
So, totally inefficient but also working:
int Set<Integer> input = ...
// Build the power-set using the method from linked question
Set<Set<Integer>> power = powerSet(input);
int size = power.size() - 1;
This is the given question:
Given a non-negative number represented as an array of digits,
add 1 to the number ( increment the number represented by the digits ).
The digits are stored such that the most significant digit is at the head of the list.
Example:
If the vector has [1, 2, 3]
the returned vector should be [1, 2, 4]
as 123 + 1 = 124.
This is my code:
public class Solution {
public ArrayList<Integer> plusOne(ArrayList<Integer> A) {
int carry = 1;
int length = A.size();
ArrayList result = new ArrayList();
for( int i = length - 1; i >=0; i-- ){
int val = A.get(i) + carry;
result.add(0,val % 10);
carry = val / 10;
}
if (carry == 1){
result.add(0,1);
}
for (int j = 0; j < result.size(); j++){
if(result.get(j).equals(0))
result.remove(j);
else
break;
}
return result;
}
}
However, in the test case:
A : [ 0, 6, 0, 6, 4, 8, 8, 1 ]
it says my function returns
6 6 4 8 8 2
while the correct answer is
6 0 6 4 8 8 2
I have no idea what is wrong with my code.
Thanks!
if(result.get(j).equals(0))
result.remove(j);
else
break;
This will fail if every other index contains a 0. Here's what happens:
0 6 0 6 4 8 8 2
^ (j = 0)
The 0 will be removed, and j is incremented by one.
6 0 6 4 8 8 2
^ (j = 1)
Then this 0 is removed as well, skipping the first 6 in your array. To fix this, change the snippet to:
if(result.get(j).equals(0))
result.remove(j--);
else
break;
This compensates for when an index is removed so that j will not skip the number immediately after any removed 0s.
Check out a similar question at Looping through and arraylist and removing elements at specified index
simpler to do just
while (!result.isEmpty() && result.get(0).equals(0)) {
result.remove(0);
}
This will keep removing the left most 0 until there is no more left most zero to be deleted.
Your last for loop is removing 0 from your result ArrayList<Integer>. After removing that loop, you will get perfect output
public static ArrayList<Integer> plusOne(ArrayList<Integer> A) {
int carry = 1;
int length = A.size();
ArrayList result = new ArrayList();
for (int i = length - 1; i >= 0; i--) {
int val = A.get(i) + carry; //2 8
result.add(0, val % 10); // 2 8
carry = val / 10;
}
if (carry == 1) {
result.add(0, 1);
}
// for (int j = 0; j < result.size(); j++) {
// if (result.get(j).equals(0))
// result.remove(j);
// else
// break;
// }
for (boolean isZero = true; isZero; ) {
isZero = result.get(0).equals(0);
if(isZero)
result.remove(0);
}
return result;
}
I am trying to implement the Hungarian algorithm in Java. I have an NxN cost matrix. I am following this guide step by step. So I have the costMatrix[N][N] and 2 arrays to track covered rows and covered cols - rowCover[N], rowColumn[N] (1 means covered, 0 means uncovered)
How can I cover the 0s with the minimum number of lines? Can anyone point me in the right direction?
Any help/suggestion would be appreciated.
Check the 3rd step of the algorithm in the Wikipedia article (section Matrix Interpretation) , they explain a way to compute the minimal amount of lines to cover all the 0's
Update: The following is another way to obtain the minimum number of lines that cover the 0's:
import java.util.ArrayList;
import java.util.List;
public class MinLines {
enum LineType { NONE, HORIZONTAL, VERTICAL }
private static class Line {
int lineIndex;
LineType rowType;
Line(int lineIndex, LineType rowType) {
this.lineIndex = lineIndex;
this.rowType = rowType;
}
LineType getLineType() {
return rowType;
}
int getLineIndex() {
return lineIndex;
}
boolean isHorizontal() {
return rowType == LineType.HORIZONTAL;
}
}
private static boolean isZero(int[] array) {
for (int e : array) {
if (e != 0) {
return false;
}
}
return true;
}
public static List<Line> getMinLines(int[][] matrix) {
if (matrix.length != matrix[0].length) {
throw new IllegalArgumentException("Matrix should be square!");
}
final int SIZE = matrix.length;
int[] zerosPerRow = new int[SIZE];
int[] zerosPerCol = new int[SIZE];
// Count the number of 0's per row and the number of 0's per column
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (matrix[i][j] == 0) {
zerosPerRow[i]++;
zerosPerCol[j]++;
}
}
}
// There should be at must SIZE lines,
// initialize the list with an initial capacity of SIZE
List<Line> lines = new ArrayList<Line>(SIZE);
LineType lastInsertedLineType = LineType.NONE;
// While there are 0's to count in either rows or colums...
while (!isZero(zerosPerRow) && !isZero(zerosPerCol)) {
// Search the largest count of 0's in both arrays
int max = -1;
Line lineWithMostZeros = null;
for (int i = 0; i < SIZE; i++) {
// If exists another count of 0's equal to "max" but in this one has
// the same direction as the last added line, then replace it with this
//
// The heuristic "fixes" the problem reported by #JustinWyss-Gallifent and #hkrish
if (zerosPerRow[i] > max || (zerosPerRow[i] == max && lastInsertedLineType == LineType.HORIZONTAL)) {
lineWithMostZeros = new Line(i, LineType.HORIZONTAL);
max = zerosPerRow[i];
}
}
for (int i = 0; i < SIZE; i++) {
// Same as above
if (zerosPerCol[i] > max || (zerosPerCol[i] == max && lastInsertedLineType == LineType.VERTICAL)) {
lineWithMostZeros = new Line(i, LineType.VERTICAL);
max = zerosPerCol[i];
}
}
// Delete the 0 count from the line
if (lineWithMostZeros.isHorizontal()) {
zerosPerRow[lineWithMostZeros.getLineIndex()] = 0;
} else {
zerosPerCol[lineWithMostZeros.getLineIndex()] = 0;
}
// Once you've found the line (either horizontal or vertical) with the greater 0's count
// iterate over it's elements and substract the 0's from the other lines
// Example:
// 0's x col:
// [ 0 1 2 3 ] -> 1
// [ 0 2 0 1 ] -> 2
// [ 0 4 3 5 ] -> 1
// [ 0 0 0 7 ] -> 3
// | | | |
// v v v v
// 0's x row: {4} 1 2 0
// [ X 1 2 3 ] -> 0
// [ X 2 0 1 ] -> 1
// [ X 4 3 5 ] -> 0
// [ X 0 0 7 ] -> 2
// | | | |
// v v v v
// {0} 1 2 0
int index = lineWithMostZeros.getLineIndex();
if (lineWithMostZeros.isHorizontal()) {
for (int j = 0; j < SIZE; j++) {
if (matrix[index][j] == 0) {
zerosPerCol[j]--;
}
}
} else {
for (int j = 0; j < SIZE; j++) {
if (matrix[j][index] == 0) {
zerosPerRow[j]--;
}
}
}
// Add the line to the list of lines
lines.add(lineWithMostZeros);
lastInsertedLineType = lineWithMostZeros.getLineType();
}
return lines;
}
public static void main(String... args) {
int[][] example1 =
{
{0, 1, 0, 0, 5},
{1, 0, 3, 4, 5},
{7, 0, 0, 4, 5},
{9, 0, 3, 4, 5},
{3, 0, 3, 4, 5}
};
int[][] example2 =
{
{0, 0, 1, 0},
{0, 1, 1, 0},
{1, 1, 0, 0},
{1, 0, 0, 0},
};
int[][] example3 =
{
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 0},
{0, 0, 1, 1, 0, 0},
{0, 1, 1, 0, 0, 0},
{0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0}
};
List<int[][]> examples = new ArrayList<int[][]>();
examples.add(example1);
examples.add(example2);
examples.add(example3);
for (int[][] example : examples) {
List<Line> minLines = getMinLines(example);
System.out.printf("Min num of lines for example matrix is: %d\n", minLines.size());
printResult(example, minLines);
System.out.println();
}
}
private static void printResult(int[][] matrix, List<Line> lines) {
if (matrix.length != matrix[0].length) {
throw new IllegalArgumentException("Matrix should be square!");
}
final int SIZE = matrix.length;
System.out.println("Before:");
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
System.out.printf("%d ", matrix[i][j]);
}
System.out.println();
}
for (Line line : lines) {
for (int i = 0; i < SIZE; i++) {
int index = line.getLineIndex();
if (line.isHorizontal()) {
matrix[index][i] = matrix[index][i] < 0 ? -3 : -1;
} else {
matrix[i][index] = matrix[i][index] < 0 ? -3 : -2;
}
}
}
System.out.println("\nAfter:");
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
System.out.printf("%s ", matrix[i][j] == -1 ? "-" : (matrix[i][j] == -2 ? "|" : (matrix[i][j] == -3 ? "+" : Integer.toString(matrix[i][j]))));
}
System.out.println();
}
}
}
The important part is the getMinLines method, it returns a List with the lines that cover the matrix 0's entries. For the example matrices prints:
Min num of lines for example matrix is: 3
Before:
0 1 0 0 5
1 0 3 4 5
7 0 0 4 5
9 0 3 4 5
3 0 3 4 5
After:
- + - - -
1 | 3 4 5
- + - - -
9 | 3 4 5
3 | 3 4 5
Min num of lines for example matrix is: 4
Before:
0 0 1 0
0 1 1 0
1 1 0 0
1 0 0 0
After:
| | | |
| | | |
| | | |
| | | |
Min num of lines for example matrix is: 6
Before:
0 0 0 0 0 0
0 0 0 1 0 0
0 0 1 1 0 0
0 1 1 0 0 0
0 1 0 0 0 0
0 0 0 0 0 0
After:
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
I hopes this give you a boost, the rest of the Hungarian algorithm shouldn't be hard to implement
I know this question has been solved long time ago, but I would like to share my implementation for the step 3 where minimum lines should be drawn in a way that all zeros are covered.
Here's a brief explanation on how my algorithm for this step works:
Loop on all cells, the cell that has a value zero, we need to draw a line passing by it, and its neighbours
To know in which direction the line should be drawn, I created a method called maxVH() that will count the zeros vertically vs horizontally, and returns an integer. If the integer is positive, draw a vertical line, else if zero or negative, draw a horizontal line.
colorNeighbors() method will draw the lines and will count them as well. Moreover, it will place 1 on the elements where the line passes vertically. -1 on the elements where the line passes horizontally. 2 on the elements where 2 intersecting lines passes (horizontal and vertical).
The advantage of having those 3 methods is that we know the elements that are covered twice, we know which elements are covered, and which are not covered. In addition, while drawing the lines, we increment the number of line counter.
For the full implementation of the Hungarian Algorithm + Example: Github
Code + Detailed Comments for step 3:
/**
* Step 3.1
* Loop through all elements, and run colorNeighbors when the element visited is equal to zero
* */
public void coverZeros(){
numLines = 0;
lines = new int[values.length][values.length];
for(int row=0; row<values.length;row++){
for(int col=0; col<values.length;col++){
if(values[row][col] == 0)
colorNeighbors(row, col, maxVH(row, col));
}
}
}
/**
* Step 3.2
* Checks which direction (vertical,horizontal) contains more zeros, every time a zero is found vertically, we increment the result
* and every time a zero is found horizontally, we decrement the result. At the end, result will be negative, zero or positive
* #param row Row index for the target cell
* #param col Column index for the target cell
* #return Positive integer means that the line passing by indexes [row][col] should be vertical, Zero or Negative means that the line passing by indexes [row][col] should be horizontal
* */
private int maxVH(int row, int col){
int result = 0;
for(int i=0; i<values.length;i++){
if(values[i][col] == 0)
result++;
if(values[row][i] == 0)
result--;
}
return result;
}
/**
* Step 3.3
* Color the neighbors of the cell at index [row][col]. To know which direction to draw the lines, we pass maxVH value.
* #param row Row index for the target cell
* #param col Column index for the target cell
* #param maxVH Value return by the maxVH method, positive means the line to draw passing by indexes [row][col] is vertical, negative or zero means the line to draw passing by indexes [row][col] is horizontal
* */
private void colorNeighbors(int row, int col, int maxVH){
if(lines[row][col] == 2) // if cell is colored twice before (intersection cell), don't color it again
return;
if(maxVH > 0 && lines[row][col] == 1) // if cell colored vertically and needs to be recolored vertically, don't color it again (Allowing this step, will color the same line (result won't change), but the num of line will be incremented (wrong value for the num of line drawn))
return;
if(maxVH <= 0 && lines[row][col] == -1) // if cell colored horizontally and needs to be recolored horizontally, don't color it again (Allowing this step, will color the same line (result won't change), but the num of line will be incremented (wrong value for the num of line drawn))
return;
for(int i=0; i<values.length;i++){ // Loop on cell at indexes [row][col] and its neighbors
if(maxVH > 0) // if value of maxVH is positive, color vertically
lines[i][col] = lines[i][col] == -1 || lines[i][col] == 2 ? 2 : 1; // if cell was colored before as horizontal (-1), and now needs to be colored vertical (1), so this cell is an intersection (2). Else if this value was not colored before, color it vertically
else // if value of maxVH is zero or negative color horizontally
lines[row][i] = lines[row][i] == 1 || lines[row][i] == 2 ? 2 : -1; // if cell was colored before as vertical (1), and now needs to be colored horizontal (-1), so this cell is an intersection (2). Else if this value was not colored before, color it horizontally
}
// increment line number
numLines++;
// printMatrix(lines); // Monitor the line draw steps
}//End step 3
This is an improvement on #higuaro's answer, but in Swift (works for
[[0,94,2,91,57,0,115,2,99],[113,19,7,32,42,13,0,35,16],[109,11,31,56,38,29,16,31,0],[81,51,39,0,10,37,24,67,40],[94,0,34,59,23,42,27,30,11],[71,37,39,0,0,47,32,71,48],[71,41,43,4,0,43,28,71,44],[80,110,0,153,137,0,113,0,97],[0,94,0,89,57,8,121,0,105]]
):
func modifiedGetMinLines(_ matrix: [[Int]]) -> Set<Line> { // O(N^4)
// Using the algorithm found here - https://www.youtube.com/watch?v=rrfFTdO2Z7I
func drawLinesWhileIsolatedZerosExist(_ matrix: inout [[Int]]) -> Set<Line> { // O(N^3)
let N = matrix.count
var lines: Set<Line> = []
var unprocessedTableChange = true
while unprocessedTableChange { // While loop occurs 2N-1 times max!...each time a line in a matrix must be crossed out to continue
unprocessedTableChange = false
for i in 0..<N { // rows
var zeroCount = 0
var columnOfLastZero = -1
for j in 0..<N {
if matrix[i][j] == 0 {
zeroCount += 1
columnOfLastZero = j
}
}
if zeroCount == 1 {
unprocessedTableChange = true
var selectedCol = Line(columnOfLastZero, .VERTICAL)
for i in 0..<N {
if matrix[i][columnOfLastZero] == 0 {
selectedCol.coord.insert(i)
}
matrix[i][columnOfLastZero] = -1 // Cross line out
}
lines.insert(selectedCol)
}
}
for i in 0..<N { // columns
var zeroCount = 0
var rowOfLastZero = -1
for j in 0..<N {
if matrix[j][i] == 0 {
zeroCount += 1
rowOfLastZero = j
}
}
if zeroCount == 1 {
unprocessedTableChange = true
var selectedRow = Line(rowOfLastZero, .HORIZONTAL)
for i in 0..<N {
if matrix[rowOfLastZero][i] == 0 {
selectedRow.coord.insert(i)
}
matrix[rowOfLastZero][i] = -1 // Cross line out
}
lines.insert(selectedRow)
}
}
}
return lines
}
func zerosToProcessExist(_ array: [Int]) -> Bool { // O(N)
for e in array {
if e > 0 { return true }
}
return false
}
var matrix = matrix
let N = matrix.count
var lines: Set<Line> = drawLinesWhileIsolatedZerosExist(&matrix) // O(N^3)
var zerosPerRow = Array(repeating: 0, count: N)
var zerosPerCol = Array(repeating: 0, count: N)
for i in 0..<N { // O(N^2)
for j in 0..<N {
if matrix[i][j] == 0 {
zerosPerRow[i] += 1
zerosPerCol[j] += 1
}
}
}
while zerosToProcessExist(zerosPerRow) || zerosToProcessExist(zerosPerCol) { // While loop occurs 2N-1 times max!...each time a line in a matrix must be crossed out to continue
var max = 0
var lineWithMostZeros: Line?
var linesWithMaxZeros: Set<Line> = []
for i in 0..<N { // O(N)
if zerosPerRow[i] > max {
linesWithMaxZeros = []
linesWithMaxZeros.insert(Line(i, LineType.HORIZONTAL))
max = zerosPerRow[i]
} else if zerosPerRow[i] == max && max > 0 {
linesWithMaxZeros.insert(Line(i, LineType.HORIZONTAL))
}
if zerosPerCol[i] > max {
linesWithMaxZeros = []
linesWithMaxZeros.insert(Line(i, LineType.VERTICAL))
max = zerosPerCol[i]
} else if zerosPerCol[i] == max && max > 0 {
linesWithMaxZeros.insert(Line(i, LineType.VERTICAL))
}
}
if linesWithMaxZeros.count == 1 {
lineWithMostZeros = linesWithMaxZeros.first
} else {
var minScore = Int.max
var minScoreLine: Line?
for l in linesWithMaxZeros {
var score = 0
if l.isHorizontal() {
for j in 0..<N {
if matrix[l.lineIndex][j] == 0 {
for k in 0..<N {
if matrix[k][j] == 0 { score += 1 }
}
}
}
} else {
for j in 0..<N {
if matrix[j][l.lineIndex] == 0 {
for k in 0..<N {
if matrix[j][k] == 0 { score += 1 }
}
}
}
}
if score < minScore {
minScore = score
minScoreLine = l
}
}
lineWithMostZeros = minScoreLine
}
let index = lineWithMostZeros!.lineIndex
var temp: Set<Int> = []
if lineWithMostZeros!.isHorizontal() { // O(N)
zerosPerRow[index] = 0
for j in 0..<N {
if matrix[index][j] == 0 {
zerosPerCol[j] -= 1
temp.insert(j)
}
matrix[index][j] = -1
}
} else {
zerosPerCol[index] = 0
for j in 0..<N {
if matrix[j][index] == 0 {
zerosPerRow[j] -= 1
temp.insert(j)
}
matrix[j][index] = -1
}
}
lineWithMostZeros!.coord = temp
lines.insert(lineWithMostZeros!)
}
return lines
}