I am working on LeetCode problem 827. Making A Large Island:
You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.
Return the size of the largest island in grid after applying this operation.
An island is a 4-directionally connected group of 1s.
Example 1:
Input: grid = [[1,0],[0,1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.
My solution fails for the following test case:
[[1,1,0],[0,0,0], [1,1,0]]
The output of my code is 3, but 5 is expected.
Here is my code:
class Solution {
public int largestIsland(int[][] grid) {
int n=grid.length;
int max=0;
boolean hasZero=false;
boolean[][] visited = new boolean[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==0){
grid[i][j]=1;
max=Math.max(max,area(grid,n,i,j,visited));
grid[i][j]=0;
// visited[i][j]=false;
hasZero=true;
}
}
}
return hasZero?max:n*n;
}
private int area(int[][] grid, int n, int i, int j, boolean[][] visited){
if(i<0 || i>n-1 || j>n-1 || j<0 || grid[i][j]==0 || visited[i][j])
return 0;
visited[i][j]=true;
int a1 = area(grid,n,i+1,j,visited);
int a2 = area(grid,n,i,j+1,visited);
int a3 = area(grid,n,i-1,j,visited);
int a4 = area(grid,n,i,j-1,visited);
return 1+a1+a2+a3+a4;
}
}
This solution is O(N^4) and I know other, more efficient working solutions, but I'm not able to spot what's wrong with my attempt.
Could someone spot what is wrong?
The problem is that after you have marked an island as visited, it can no longer play a role in a better connection.
For instance, your failing test case:
[[1, 1, ],[0, 0, 0],[1, 1, 0]]
...can be depicted as:
1 1 0
0 0 0
1 1 0
Your code will first try this (changing the value in brackets):
1 1(1)
0 0 0
1 1 0
... and mark those as visited (I'll mark those with "v"):
v v v
0 0 0
1 1 0
...and so it finds 3 for max.
Then it will continue with finding the following:
v v v
(1)0 0
1 1 0
This will lead to a value of 3, which does not improve the previous value for max. But this is wrong, because it really connects with another island that you had marked visited. It should have found this:
1 1 0
(1)0 0
1 1 0
... which is 5.
As you already found working algorithms, I suppose this answers your question.
Related
I have a sudoku puzzle 9x9 in a text file and I wondering how can we create a Graph from sudoku puzzle.Sudoku puzzle is a int[][] Example puzzle
0 0 0 0 9 8 0 4 5
0 0 4 3 2 0 7 0 0
7 9 0 5 0 0 0 3 0
0 0 0 9 0 0 4 0 0
0 4 5 0 0 2 8 0 0
8 7 9 6 0 4 0 1 0
0 3 0 0 7 9 0 6 4
4 5 0 2 1 3 9 0 8
0 8 7 4 6 5 0 0 0
and class Graph
class Graph
{
private int V;
private LinkedList<Integer> adj[];
Graph(int v)
{
V = v;
adj = new LinkedList[v];
for (int i=0; i<v; ++i)
adj[i] = new LinkedList();
}
void addEdge(int v,int w)
{
adj[v].add(w);
adj[w].add(v);
}
public int getV()
{
return V;
}
public LinkedList<Integer> getListAdj(int u)
{
return adj[u];
}
I write a function to read puzzle from a text file and implement it to graph
public boolean readGraph(String input_name, int result[])
{
return true;
}
But I stuck in this step.
Here goes:
First, I already know that the puzzle is 9x9, so the newlines in the text file are meaningless to me. I can ignore them. Also, I know that each element of the puzzle is only ever going to be a single character long so (after getting the text file into memory like so: Reading a plain text file in Java):
I get my String puzzle which is that text file into memory. Now I want to iterate over that String like so:
Graph graph = new Graph(81); //9x9 graph with 81 verticies
int whichVertextAreWeOn = 0;
for (int i = 0; i < puzzle.length(); i++){
char c = puzzle.charAt(i);
if(c>'0' && c > '9'){
int integer = Integer.parseInt(c);
//now I need to add this to my Graph... Saving my work now, comments are appreciated
//Turns out you simply add edges to each vertex in a graph, the vertex itself has no value...
//so I'm going to keep track of which vertex I'm on, this is starting to seem like a bad data structure for this purpose, but I shall carry on. -Adding an iterator to keep track of which vertex I'm on.
for(int w = 0; w < graph.V(); w++;){
if(vertextIsLinked(whichVertextAreWeOn, w))
graph.addEdge(whichVertextAreWeOn, w);
}
//hum... from here I need to add an edge to this vertex for each connected vertex...
}
}
private boolean vertextIsLinked(int vertexWeAreOn, int possibleVertext){
//use rules of Sukdoku to figure out if these two verticies should be linked. This would return true for any vertex in horizontal or vertical alignment to the vertexWeAreOn.
}
public class IntegerSet {
int dMax;
boolean[] set;
public IntegerSet(int domainMax) {
dMax = domainMax + 1;
set = new boolean[dMax];
}
...some methods...
public static IntegerSet union(IntegerSet s1, IntegerSet s2) {
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
union.set[i] = (s1.set[i] || s2.set[i]);
}
return union;
}
Can anyone tell me whats wrong with this?
I can't understand why I'm getting the error message after hours: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
As the two sets supplied as arguments may have different domain max; the resulting union should have whichever of these two domain max is larger hence why I am using Math.max.
Any help would be appreciated.
The problem arises when s1 and s2 have different lengths. These lines are flawed:
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
Think about what two sets of unequal length look like:
(using 1 and 0 in place of true and false)
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
You've programmed your loop to iterate from 0 up to the maximum length of the two sets, which in the above example is 12 (meaning that the last value your loop will use is 11). But s1 is only 6 elements long--its last valid index is 5! As the loop erroneously attempts to access elements 7 through 12 (indices 6 through 11) of s1, it throws an ArrayIndexOutOfBoundsException.
To fix this, you should use the minimum length of the two sets in both of the lines where you use the maximum. This way, you will be taking the union:
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1} length = min(6, 12) = 6
Instead of
s1: {0 1 0 0 1 1}! ! ! ! ! ! length = 6; no elements 7 through 12!
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1 ! ! ! ! ! !} length = max(6, 12) = 12 -> errors
This leaves the later section of s2 out of the union, as there are no corresponding elements in s1 to perform a boolean || with. However, you could also do something like this:
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
if (s2.set.length > s1.set.length)
{
union.set[i] = s2.set[i] || (i < s1.set.length ? s1.set[i] : false);
}
else
{
union.set[i] = s1.set[i] || (i < s2.set.length ? s2.set[i] : false);
}
}
This will use false's for every missing element in the shorter set, resulting in the union being:
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1 1 0 1 1 0 1} length = max(6, 12) = 12
All entries are simply copied from the longer set, since anything || false is itself.
This:
dMax = domainMax + 1;
should be:
dMax = domainMax;
Or just use set.length.
Your for-loop goes from 0 to the max size of s1 and s2. But because (in the situation that throws errors), one of your IntegerSets is smaller, the for-loop goes past the size of the smaller IntegerSet when retrieving all of the booleans from the larger IntegerSet
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
union.set[i] = (s1.set[i] || s2.set[i]);
}
If the size of one set is smaller than the max, then you are going to get an IndexOutOfBoundsException.
Example:
Say you have the following conditions:
s1 = {0, 1, 1, 0, 1}
s2 = {0, 0, 0}
s1.dMax = 5, so s1.set has a size of 5
s2.dMax = 3, so s2.set has a size of 3
In your for-loop, you are going to iterate from 0 to 4. When the following occurs:
i = 3
s1.set[i] = s1.set[3] //IndexOutOfBoundsException because the size of s1.set is 3.
You have a couple options of how you can fix this:
You can add a check in your for-loop to ensure that you aren't
going to get an IndexOutOfBoundsException for your smaller
IntegerSet and just set the index in union to the boolean value of your larger set.
You can pad to your smaller IntegerSet depending on what you want the
union to be for the blank indicies.
Use the min size of the two IntegerSets if you want to ignore the
extra booleans (Judging from the fact that you made the union set
the size of the max, I am guess that is not the case)
Side Note: You also do not need to add the +1 to the dMax variable when you create your union IntegerSet. That is going to result in your having an extra index that you don't need and could cause problems later.
You are using dMax as the number of boolean array elements in one place, and as the domain maximum (wrong) when you create the new union set. (That causes you to make union 1 element too large.)
Several things to consider when you rewrite your code:
Assuming domainMax implies a set of integers from [0, domainMax], do define a boolean array with domainMax+1 elements (don't bother with a dMax variable, you can get that info by checking set.length
In the "union" function, be sure to handle the case where one or both of the input IntegerSets is null
Use protected set to make sure the internal representation of the IntegerSet is not known or accessed outside your class
When you perform the union of two non-null IntegerSets, only perform the or operation (||) for the case where both sets have values; then for the "bigger" set, simply copy over the remaining items.
For example, say we have a Sudoku board like this:
0 0 6 5 8 9 7 4 3
0 5 0 0 0 0 0 6 0
7 0 9 0 6 0 1 0 0
0 3 0 0 0 2 0 8 7
0 0 1 0 0 0 4 0 0
8 9 0 6 0 0 0 5 0
0 0 2 0 5 0 3 0 6
0 7 0 0 0 0 0 9 0
3 1 8 4 9 6 5 0 0
I want to store it into one array such that the first 9 elements of the array are the first sub block, i.e. the values {0 0 6 0 5 0 7 0 9} and followed by {5 8 9 0 0 0 0 6 0}.
I've tried finding a solution but I always get an array index out of bounds error and it is too brute force. Something similar to this:
while(st.hasMoreTokens()) {
if(ctr == 27) {
c.addSubBlock(sb1);
c.addSubBlock(sb2);
c.addSubBlock(sb3);
sb1 = new SubBlock();
sb2 = new SubBlock();
sb3 = new SubBlock();
ctr = 0;
}
sb1.addElement(Integer.parseInt(st.nextToken()));
sb1.addElement(Integer.parseInt(st.nextToken()));
sb1.addElement(Integer.parseInt(st.nextToken()));
sb2.addElement(Integer.parseInt(st.nextToken()));
sb2.addElement(Integer.parseInt(st.nextToken()));
sb2.addElement(Integer.parseInt(st.nextToken()));
sb3.addElement(Integer.parseInt(st.nextToken()));
sb3.addElement(Integer.parseInt(st.nextToken()));
sb3.addElement(Integer.parseInt(st.nextToken()));
ctr+=9;
}
Please give me some tips. Code snippets would also be a great help.
EDIT: This thread somehow helped me figured it out. And yes, this is part of the Sudoku where I'm trying to encode the board into an array.
What I did was to transform first the input String into a 2d array (9x9) and use int block = (row/3)*3 + (col/3); to compute exactly which sub block each element belongs.
Create a 3x3 array of sub blocks
Use 2 counters (x & y) for tracking the position in the full board of each element read
Add the values at (x,y) into sub block (x/3,y/3)
Something like this:
SubBlock board[][] = new SubBlock[3][3];
int x, y;
for ( y=0; y<9; y++ )
for ( x=0; x<9; x++ )
board[y/3][x/3].addElement(Integer.parseInt(st.nextToken()));
board[0][0] will be the top-left SubBlock, board[2][2] the bottom-right one.
Store everything in a two dimension array. E.g.
int[] board = {
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}
{1,2,3,4,5,6,7,8,9}};
//looping
public static void Main(string[] args){
for(int i = 0; i < 9; i++)
{
System.out.println("SubBlock number"+i);
for(int j = 0; j < 9; j++){
System.out.println(board[i][j]);
}
}
}
Assuming that you are reading input left to right, top to bottom, try a set of 4 nested loops like this:
int board[] = new int[81];
for (int outY = 0; outY < 3; outY++)
{
for (int outX = 0; outX < 3; outX++)
{
for (int inY = 0; inY < 3; inY++)
{
for (int inX = 0; inX < 3; inX++)
{
board[27*outY + 9*outX + 3 * inY + inX] = //parse an int from your input here
}
}
}
}
It would be great if we knew why you are trying to loop through the board.
If you want to check if you can enter a number, I recommend you use maps for each of the 3x3 squares.
Then check if the item is in the map already or not. For the rows and columns, you can either loop over the 2D array and check each element, or -again- use a map for each column and a map for each row.
I'm not entirely sure if you want an answer for a single-dimension array or if you're willing to make it a two-dimensional array (as you mention each nine element set with curly braces), but if two-dimensional is OK...
The OP in this Code Review posting used a 'fancy' way of sifting through the subgrids by using the math (i % 3) + rowStart inside one of the square brackets and (i / 3) + colStart inside the other. One commenter noted this modulo method to be a bit obscure, and I'm prone to agree, but for how clean it is and the fact that it works, I think it's a solid solution. So, paired with the iteration of the for loop, we can sift through each 'subgrid' cell, as well as each element of row + col.
for(i=0; i<9; ++i)
{
if (puzzle[row][i] == num) return 0;
if (puzzle[i][col] == num) return 0;
if (puzzle[rowStart + (i%3)][colStart + (i/3)] == num) return 0;
}
If we find a number in one of the cells that matches, it's a duplicate, and we exit the function as 'false', or, 0.
EDIT:
Now that I think of it, you could use this same trick for a single-dimension array by using i % 9 instead of 3. You could then determine which 'row' we're on by doing i / 9 and trusting that, since we're dealing with type ints, we'll truncate the unnecessary decimals.
This does verify that this trick is a bit prone towards N-1 indexed data, as someone would assume 'go to the 81st element' would mean go to the 9th column of the 9th row, but using 81 % 9 would yield 0, and 81 / 9 would yield 9, so we'd go to the 0th place at row 9.
I ran into a StackOverflowError when running a solution I wrote to an assignment.
These are the exact instructions from the book Java Methods: A & AB:
Write a program in which Cookie Monster finds the optimal path from the upper left corner (0,0) to the lower right corner(SIZE-1,SIZE-1) in a cookie grid (a 2-D array). The elements of the grid contain cookies (a non-negative number) or barrels (-1). On each step, Cookie Monster can only go down or to the right. He is not allowed to step on barrels. The optimal path contains the largest number of cookies.
The program reads the cookie grid from a file and reports the number of cookies on the optimal path. (The path itself is not reported.) A sample data file is provided in JM\Ch19\Exercises\cookies.dat.
Hint: Use a stack. If there is only one way to proceed from the current position, then go there and update the total accumulated number of cookies. If there are two ways to proceed, save one of the possible two points (and its total) on the stack and proceed to the other point. If you have reached the lower-right corner, update the maximum. If there is nowhere to go, examine the stack: pop a saved point, if any, and resume from there.
The goal is to give my teacher the best possible path (the one with the most "cookies" on it).
Okay. so the mentioned cookie map file is this:
1 3 0 5 -1 7 -1 -1 0 4 2 1
-1 3 2 1 -1 4 -1 5 3 -1 1 0
5 4 8 -1 3 2 2 -1 4 -1 0 0
2 1 0 4 1 -1 8 0 2 -1 2 5
1 4 0 1 -1 0 3 2 2 4 1 4
0 1 4 1 1 6 1 4 5 2 1 0
3 2 5 2 0 7 -1 2 1 0 -1 3
0 -1 4 -1 -1 3 5 1 4 2 1 2
5 4 8 -1 3 2 2 -1 4 -1 0 0
2 1 0 4 1 -1 8 0 2 -1 2 5
1 3 0 5 -1 7 -1 -1 0 4 2 1
0 0 3 1 5 2 1 5 4 1 3 3
And this is the class I use to get a 2-D array of the numbers (I know this part works.) Using A BlueJ debugger, the 2-D array seems to be what I want it to be.
import java.util.*;
import java.io.*;
public class MapReader
{
public static int[][] grid;
public static Scanner gridscanner = null;
public static int[][] getMap()
{
File file = new File("cookies.dat");
try
{
gridscanner = new Scanner(file);
}
catch (FileNotFoundException ex)
{
System.out.println("*** Cannot open cookis.dat ***");
System.exit(1);
}
int row = 12;
grid = new int[row][row];
for(int r = 0; r < row; r++)
{
for(int c = 0; c < row; c++)
{
grid[r][c] = gridscanner.nextInt();
}
}
return grid;
}
}
And here is a class I use to keep track of saved positions, their values, and their locations for when I'm traversing this "cookie map":
import java.util.*;
public class Point
{
int row;
int col;
int total;
public Point(int r, int c, int t)
{
row = r;
col = c;
total = t;
}
public int getRow() { return row; }
public int getCol() { return col; }
public int getValue() { return MapReader.getMap()[row][col]; }
public int getTotal() { return total; }
}
And finally, here is the class that I use to recursively travel through the 2D array. You'll notice that I prefer to go right when two paths are available but then go down when I pop a Point from the "saved" Stack. The problem lies in this class as far as I know: How can I make the method end its recursion and reach a maximum?
import java.util.*;
public class CookieMonster
{
private static int[][] map = MapReader.getMap();
private static int max = 11;
private static int total, maximum;
private static Stack<Point> saved = new Stack<Point>();
public static void main(String[] args)
{
System.out.println(move(0,0));
}
public static int move(int r, int c)
{
int right = 0;
int down = 0;
boolean isright = true;
boolean isdown = true;
if (c < max)
{
right = map[r][c + 1];
}
else
isright = false;
if (r < max)
{
down = map[r + 1][c];
}
else
isdown = false;
if (right == -1)
isright = false;
if (down == -1)
isdown = false;
if (isright && isdown)
{
saved.push(new Point(r + 1, c, total + down));
total += right;
move(r, c + 1);
}
else if (isright)
{
total += right;
move(r, c + 1);
}
else if (isdown)
{
total += down;
move(r + 1, c);
}
else
{
if (r == max && c == max)
{
if (maximum < total)
maximum = total;
}
if (!saved.isEmpty())
{
Point sd = saved.pop();
total = sd.getTotal();
move(sd.getRow(), sd.getCol());
}
}
return maximum;
}
}
I know the hint suggests to use a stack, but this problem can be solved much more efficiently using dynamic programming. It is basically recursion with a memory of previously visited paths to avoid recomputation.
Assuming you index the matrix starting at 1, Your cost function should be as follows:
c(i, j) = -INF if i == 0 or j == 0 or data(i, j) < 0
data(1, 1) if i == 1 and j == 1
data(i, j) + min(c(i, j - 1), c(i - 1, j))
You can iterate in the usual nested i-j loop from left to right and up to down.
c(n, n) will give you the result of the optimal path.
This seems np-complete. Using a stack and writing the logic to return to a former path if you run into a dead-end seems like wasted effort. You are going to have to use a lot of brute force even with the stack and logic. It seems easier to just use a primitive to store the number of cookies and just write some logic to move down and right at random. If you hit a dead end, just throw that result out and start over. If you hit the end, save that value and check to see if it is larger than a previous path. It if it is, keep it until you find a larger one. If you run it enough times, you will find the best path. I can't imagine it would take more than a few seconds to find the most cookies.
This is a brute force attempt at the chromatic number of a matrix. It seems to work in the sense that it gives me the correct number of colors required but instead of 1,2,3,4 it will display 1,2,3,6. For some reason even if the matrix is possibly successful with fewer colors it will still fail and keep going to the max number. Is there a reason why it continues to fail?
"n" is the max number of colors. "v" is the vertex, and "m" is the number of currently used colors.
pseudo code:
http://i42.tinypic.com/deoc2u.jpg
static int color(){
int i;
for(i = 1; i <= n; i++)
{
if(color(0,i)){
return i;}
}
return i;
}
static boolean color(int v, int m) {
if(v > n-1)
return true;
else
{
for(int i = 1; i <= m; i++)
{
boolean match = false;
q[v] = i;
for(int j = 0; j < n; j++)
{
if(input[v][j] == 1)
{
if(q[v] == j+1)
match = true;
}
}
if(match == false)
{
if(color(v+1,m))
return true;
}
}
q[v] = 0;
return false;
}
}
sample output:
File Name: file1
Input
6
0 1 1 0 1 1
1 0 1 1 1 1
1 1 0 1 0 1
0 1 1 0 1 1
1 1 0 1 0 1
1 1 1 1 1 0
1 failed
2 failed
3 failed
4 failed
5 failed
Colors:
1 2 3 1 3 6
Gah, I should be asleep! But I can't count the number of times I've put off an assigment to the absolute last possible moment, so I'm feeling sympathetic regarding your deadline.
It's not really a "brute-force" approach, since that would actually come down to trying each and every possible node coloring combination and check which ones don't conflict. Your approach is a heuristic known as greedy coloring. It can find optimal results but can also yield an arbitrarily bad solution. That said, I've tried manually going through the sample input you've provided (thank you, Microsoft Paint) and following that algorithm the result should indeed be 4 when starting from vertex 0.
So here's what I believe might be wrong with your code. In the following extract...
if(input[v][j] == 1)
{
if(q[v] == j+1)
match = true;
}
You seem to be comparing the color of the current vertex (which will just be i anyway) to a vertex index, rather than the color of that other vertex. I think you'll need to alter the inner test to...
if(i == q[j])
or, if you wish (won't make a difference)...
if(q[i] == q[j])
Also, you're doing a bit too many checks. Vertex 0 can be assigned color 1. Vertex 1 then needs to be checked against vertex 0 if it's adjacent. Vertex 2 needs to be checked against 0 and 1 if they're adjacent, and so on. You're checking against vertices that couldn't have been assigned a color yet. So don't limit j to n but limit it to your current vertex v.
Finally, using a construct like if(match == false) is rather confusing and not necessary. Just use if(!match) instead.
Hope this helps. If you're still stuck and I happen to catch the comments in time, I can provide more pointers.