1

Basically this is a homework assignment and if you answer my question, I prefer to get a lead to the answer rather than the code and the answer itself.

This has to be a recursive method, which, in a given two dimensional boolean array, has to return the number of true zones in the array - the number of rows and cols in the array will always be the same.

True zone is defined when there's at least one true element, if it has a neighboring other element which is also true, they still count as 1. Elements that are diagonal are not considered neighbors.

For example, in this matrix when 1 stands as true and 0 stands as false, there are 3 true zones - the two cells on the left side, the three cells on the right side, and the one cell on the last row, by himself.

1 0 0 1
1 0 1 1
0 1 0 0

I don't know how to approach this problem recursively, in a simple iteration it'd be quite simple I assume, but it seems impossible to check the array using a method that's calling itself.

Anyone has a lead?

halfer
  • 19,824
  • 17
  • 99
  • 186
Ohande
  • 55
  • 5
  • 1
    recursion is not backtracking. sometimes a backtracking computational structure is built with recursion -- like building *n* nested loops through recursion to perform [tag:recursive-backtracking]; but recursion is a thing in itself. there *is* a *naturally* recursive solution here. as for the backtracking -- i.e. re-trying after failure until success -- I don't see what needs retrying here. you just split a thing in two, count your matter of interest in each (or one?), then add two things together, possibly adjusting the count. – Will Ness Dec 29 '20 at 17:43
  • so if you insist on a recursive *backtracking* solution, I have no answer to *that.* – Will Ness Dec 29 '20 at 17:56
  • @WillNess thank you for answering. I'm not required for a backtracking solution, I just figured since it was the last thing we studied, it should be the answer, but it's not a necessity. – Ohande Dec 30 '20 at 10:34
  • try breaking it up into first row and rest of rows. produce two things for the rest of rows -- the count and the "active points" atop second row, somehow. then combine that with the first row, and produce the new count and new active points atop the first row. that's the vague outline of how it seems to be it could be done. I think the answer by Judge hints at that way also, but I don't understand why it speaks of both vertical and horizontal split (or I've misunderstood it). – Will Ness Dec 30 '20 at 13:01

3 Answers3

1

These are essentially connected components, use DFS.

Andrew Vershinin
  • 1,958
  • 11
  • 16
  • your link describes an iterative algorithm, not a recursive one: "In either case, a search that begins at some particular vertex v will find the entire component containing v (and no more) before returning. To find all the components of a graph, ***loop** [emphasis mine --wn]* through its vertices, starting a new breadth first or depth first search whenever the loop reaches a vertex that has not already been included in a previously found component." – Will Ness Dec 29 '20 at 17:34
  • @WillNess Yes, but the main algorithm is recursive, the task doesn't say they cannot use loops at all. Or am I wrong? – Andrew Vershinin Dec 30 '20 at 00:19
  • @AndrewVershinin Yes, I didn't mention that but I cannot use loops at all, in the method itself or in any other methods regarding the answer. – Ohande Dec 30 '20 at 10:39
1

Neither elegant nor performant, my first attempt would be to try to simulate iteration through all fields via recursion, and for each field return 1 if it is a true zone. Also passing an array to keep track of already checked fields. Summing the results of the child calls.

// spoiler alert 

















































public class Minefield {
  public static void main(String[] args) throws Exception {
    int[][] field = { //
        { 1, 0, 0, 1 }, //
        { 1, 0, 1, 1 }, //
        { 0, 1, 0, 0 }, //
    };
    int w = field[0].length;
    int h = field.length;

    int count = count(0, 0, w, h, true, field, new int[h][w]);
    System.out.println("true zones: " + count);
  }

  private static int count(int x, int y, int w, int h, boolean checkForNewZone /* false: mark zone */, int[][] field, int[][] marked) {
    if(x < 0 || x >= w || y < 0 || y >= h) {
      return /* out of bounds */ 0;
    }

    if(checkForNewZone) {
      int count = 0;
      if(field[y][x] == 1 && marked[y][x] == 0) {
        // a new true zone -> count it
        count++;
        // and mark it
        count(x, y, w, h, false, field, marked);
      }

      // iterate to the next field
      // x++;
      // if(x >= w) {
      //   x = 0;
      //   y++;
      // }
      // count += count(x, y, w, h, true, field, marked);

      // check neighboring fields (right & down should cover everything, assuming the starting point is the top left)
      count += count(x + 1, y, w, h, true, field, marked);
      count += count(x, y + 1, w, h, true, field, marked);
      return count;
    }
    else {
      // mark zone
      if(field[y][x] == 1 && marked[y][x] == 0) {
        marked[y][x] = 1;
        count(x + 1, y, w, h, false, field, marked);
        count(x - 1, y, w, h, false, field, marked);
        count(x, y + 1, w, h, false, field, marked);
        count(x, y - 1, w, h, false, field, marked);
      }

      return 0;
    }
  }
}
Reto Höhener
  • 5,419
  • 4
  • 39
  • 79
  • why simulate iteration when there is a naturally recursive algorithm and OP asks for one? – Will Ness Dec 29 '20 at 17:35
  • No reason. It was the best I could do. I am also interested in seeing the natural solution to learn from it. – Reto Höhener Dec 29 '20 at 18:08
  • as an answer above suggests we just need to find a natural way to break this problem into parts somehow, solve each / one of those subparts, then find a way to combine the result(s) back. my current feeling is that this should be possible by splitting off a row and the rest; solving each (the bigger part *recursively*); then combining the results. the combination is not simply addition of two counts of course. – Will Ness Dec 29 '20 at 18:10
  • that answer though suggest two spittings AFAICT, one is splitting off a row and the other a column, and I can't see why that should be the case or what to do with the both of them at once. ("above" in chronological order; I use that order on SO nearly always) – Will Ness Dec 29 '20 at 18:12
  • I edited to more of a 'growing' algorithm istead of line-wise 'iteration'. Is that now a 'real' recursive algorithm? – Reto Höhener Dec 29 '20 at 18:25
  • Java is too much too verbose for me, sorry. :) but if, given n rows, you solve the (n-1) rows recursively, then produce the final result somehow from that recursive sub-result and the first row, then yes that would be a *real* recursive solution. – Will Ness Dec 29 '20 at 18:28
0

Typical recursive algorithms consist of two pieces:

  1. a base case
  2. an "inductive step"

Start by determining the base cases. These will likely be identified by the size of the input array. E.g., a 0x0 array has no true zones. Sometimes there are multiple base cases.

Then determine how, given the answer to a smaller version of the input, you can calculate the answer to a larger version of the input. This might look like considering how to go from an nxm array with its number Z of zones to an (n+1)xm array with Z' zones and an nx(m+1) array with Z'' zones. Often it is necessary to solve a slightly more complex problem in order to be successful, because you can assume more about the answer to the smaller input.

It's usually pretty easy to translate these two cases into a recursive program.

Judge Mental
  • 5,209
  • 17
  • 22
  • why go in two directions when all you need is one, and going in two is complicated and leads nowhere? but the general advice is sound; we *just* need to identify how to *split* the problem and how to *combine the results back*... and that is the whole question. – Will Ness Dec 29 '20 at 17:36