2

I'm studying N-Queens to implement it on my own, and came across the following implementation with the rules:

The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.

For example, There exist two distinct solutions to the 4-queens puzzle:

[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

And implementation (the idea is to remember the busy columns and diagonals and recursively try to put the queen into the next row):

public class Solution {

    private void helper(int r, boolean[] cols, boolean[] d1, boolean[] d2, 
                        String[] board, List<String[]> res) {
        if (r == board.length) res.add(board.clone()); //HERE
        else {
            for (int c = 0; c < board.length; c++) {
                int id1 = r - c + board.length, id2 = 2*board.length - r - c - 1;//HERE
                if (!cols[c] && !d1[id1] && !d2[id2]) {
                    char[] row = new char[board.length];
                    Arrays.fill(row, '.'); row[c] = 'Q';
                    board[r] = new String(row);
                    cols[c] = true; d1[id1] = true; d2[id2] = true;
                    helper(r+1, cols, d1, d2, board, res);
                    cols[c] = false; d1[id1] = false; d2[id2] = false;
                }
            }
        }
    }

    public List<String[]> solveNQueens(int n) {
        List<String[]> res = new ArrayList<>();
        helper(0, new boolean[n], new boolean[2*n], new boolean[2*n], 
            new String[n], res);
        return res;
    }
}

And my question is (located where commented: //HERE), what's the reason for initializing and how are the following working the way they are: id1 = r - c + board.length, id2 = 2*board.length - r - c - 1; (what do r, id1, and id2 represent?), and what's the following meant for: if (r == board.length) res.add(board.clone());? Examples would really help.

Thank you in advance and will accept answer/up vote.

EDIT

With input n as 4, would like to System.out.print the answer in the form of :

[
 [".Q..",  // Solution 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // Solution 2
  "Q...",
  "...Q",
  ".Q.."]
]

How can I do so?

Jo Ko
  • 7,225
  • 15
  • 62
  • 120

1 Answers1

3

Keeping in mind

the idea is to remember the busy columns and diagonals and recursively try to put the queen into the next row

r is the current row, which starts at 0 (helper(0, ...)) and increments in each recursion (helper(r+1, ...)).

id1 and id2 is a number that identifies \ and / diagonals. For example, the fields on the main \ diagonal 0,0-1,1-2,2-...-7,7 all have the same id1 of 8.

cols, d1 and d2 are tracking which columns and diagonals are threatened by the queens so far on the board. If you place a queen at 0,0, then cols[0] (0-th column), d1[8] (8-th \ diagonal) and d2[15] (15-th / diagonal) are all true.

This is a recursive function (calls itself). In order for a function to be both recursive and not useless, it always needs to have two different cases: a base case (also called the terminating case), and a general case (also called the recursive case). The first tells you when to stop; the second tells you how to keep going. The first tells you the simplest case; the second tells you how to break a complex case into a simpler one.

if (r == board.length) res.add(board.clone()); is the terminating case here. It says: "if we've reached the past the last row, this board as it stands now is a solution; add it to the list of results (instead of processing the next row, which wouldn't even exist)".

clone is used so that a snapshot of the current board is added instead of the reference to the current board itself (otherwise you'd end up with a bunch of references to the last board attempted).

EDIT: To me the derivation of id1 and id2 is kind of intuitive, so I am not sure I can explain it. Just try to calculate it for different fields, and you'll see how they both give a number from 1 to 15 for board size 8. Here's what they look like (in JavaScript, so I can show it here; click the blue "Run code snippet" button):

function drawTable(id, size, cb) {
  var $table = $('#' + id);
  var $tr = $('<tr>').appendTo($table);
  $('<th>').text(id).appendTo($tr);
  for (var c = 0; c < size; c++) {
    $('<th>').text(c).appendTo($tr);
  }
  for (var r = 0; r < size; r++) {
    var $tr = $('<tr>').appendTo($table);
    $('<th>').text(r).appendTo($tr);
    for (var c = 0; c < size; c++) {
      var n = cb(r, c, size);
      var $td = $('<td>').text(n).attr('data-d', n).appendTo($tr);
    }
  }
}

var size = 8
drawTable('id1', size, function(r, c, size) { return r - c + size; });
drawTable('id2', size, function(r, c, size) { return 2 * size - r - c - 1; });
th, td {
  text-align: center;
  border: 1px solid black;
  width: 2em;
}
table {
  border-collapse: collapse;
}
#id1 td[data-d="8"] {
  background-color: yellow;
}
#id2 td[data-d="15"] {
  background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="id1"></table>
<br>
<table id="id2"></table>

Yellow cells show you 8th id1 and 15th id2 - the diagonals for the field 0,0. You don't need to check the rows, because the program only ever puts one queen into each row, then goes on to the next one.

Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Thank you so much for the explanation. Before accepting the answer and upvote, would really appreciate a bit more clarification. Is `r - c + board.length` and `2*board.length - r - c - 1` just general formulas for identifying diagonals? Just wondering how they work the way they do. With id1 and id2, do they basically represent the number representing that particular row? If you can visually show where the 8th and 15th diagonals are located, would really help and appreciate it. And as for tracking which columns and diagonals are threatened, aren't we supposed to check for rows too? – Jo Ko Dec 07 '16 at 21:21
  • Checking in to see if you've see the previous comment. Please let me know. Thanks – Jo Ko Dec 08 '16 at 08:50
  • Thanks for clearing that up. One more question, as for tracking which columns and diagonals are threatened, aren't we supposed to check for rows too? – Jo Ko Dec 09 '16 at 01:55
  • 1
    Already addressed, last sentence of the answer as it currently stands. – Amadan Dec 09 '16 at 01:56
  • Missed it. Thank you so much! Really helped. – Jo Ko Dec 09 '16 at 02:02
  • One last question, after each recursion, what's the reason for cols[c] = false;, d1[id1] = false;, and d2[id2] = false;? – Jo Ko Dec 09 '16 at 02:12
  • 1
    The way this works is, for `k`-th row, we try placing a queen on the first column and then see how it turns out with the remaining rows; then we place on second column and see how it turns out with the remaining rows; etc. In order to do that, we set up the threatened column and diagonals for the current column we're trying, try for that column, then undo the threats so we can set them up for the next column. – Amadan Dec 09 '16 at 04:25
  • One last thing if you don't mind! I can't seem to figure out how to console log the answer in with the driver. Could you lend me a hand? The example for the answer is in the original post. Thanks in advance! – Jo Ko Dec 14 '16 at 06:09
  • Sorry, I don't understand what you're asking. – Amadan Dec 14 '16 at 06:11
  • Oh. There is no built-in method that can output such format; you need to write it yourself. Please try to do it yourself first. Think of how many loops you need, what to do in each step of each loop, and it should be fine. If you have problems, post as a new question, and don't forget to include the code of your attempt. – Amadan Dec 14 '16 at 09:00