4

I'm using C++, but my question is more about algorithms than implementation.

The problem is the following:

Write a program that inputs two integers n and k, where n>=k. Your program should calculate the number of different ways that k bishops could be placed on an nXn chessboard.

My basic idea is to represent each bishop as a struct with an X value and a Y value. Then I place the bishops on the board to get a configuration.

I have written a method called moveToNextPlace that allows me to move a bishop into the next available spot. I return a string to help with debugging.

struct bishop {
int y=0;
int x=0;
string moveToNextPlace (int n){
    if (y<n-1) {y++; return "move to next y value";}
    else if (x<n-1) {x++; return "move to next x value";}
    else {reset(); return "reset";};
}
void setValuesLike (bishop b){
    y=b.y;
    x=b.x;
}
void reset (){
    y=0;
    x=0;
}
bool clashesWith (bishop b){
    if (b.x==x && b.y==y){
        return true;
    }
    if ( b.y-y == b.x-x ) return true; //if their slope is 1
    return false;

}
};

I then set the board to an initial configuration by calling findSolutions with my desired settings.

int findSolutions (int k, int n){ //k bishops on n*n board
bishop *b = new bishop [k];
for (int i=0; i<k; i++){
    findAspot (b, n, i);
}
}

bool check (int num, bishop b[]){
for (int i=0 ; i<num; i++){
    if (b[i].clashesWith (b[num])) return false;
}
return true;
}

void findAspot (bishop b[], int n, int num){ //n=boardsize
while (1){
    if (check(num, b)){return;}
    if (b[num].moveToNextPlace(n) == "reset") break;
}
b[num-1].moveToNextPlace(n);
findAspot (b, n, num-1);
b[num].setValuesLike ( b[num-1] );
findAspot (b, n, num);

}

I then want to keep backtracking until I have a total number of solutions, but I get stuck on how to find the next solution.

I thought I could write a findNextSolution that keeps getting called at the end of the findSolutions function until it reaches a cycle. But I don't know what algorithm to use to find the next solution.

Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
  • 3
    Hardly. Compare http://programmers.stackexchange.com/help/on-topic with http://stackoverflow.com/help/on-topic. One specifically mentions algorithms, the other calls anything implementation-related off-topic. Programmers is for general software methodology and process stuff. OP is on-topic. – BadZen Apr 24 '15 at 00:52
  • 1
    @Yigal - Do you mean "placed on an (n,n) chessboard such that none checks any other by the usual chess rules", perhaps? – BadZen Apr 24 '15 at 00:54
  • @Badzen I would assume so because if you can just place them anywhere the answer is pretty easy. – meneldal Apr 24 '15 at 01:35
  • @YigalSaperstein do you assume each row / column can only exist 1 bishop at most? – shole Apr 24 '15 at 03:44
  • Did I answer the question to your satisfaction? – Michael Laszlo Apr 24 '15 at 06:48
  • @shole no... each row can have more than one bishop. this is why an 8 queens like representation won't work. – Yigal Saperstein Apr 24 '15 at 19:32

1 Answers1

1

You're off to a good start with your idea of storing the bishop positions in an array. This is a compact representation of a board state.

You'll have to correct your method of checking whether one bishop clashes with another. Bear in mind that two clashing bishops may be separated by a vertical distance dy and a horizontal distance dx such that dx == -dy. Therefore, you will want to compare the absolute values: the bishops clash if abs(dx) == abs(dy).

Now on to the general problem of counting the number of board states in which k bishops are arranged without clashing. You'll want to define a function that returns an integer value. Let's say that this function looks like

count(currentBishops, numRemaining)

where currentBishops is a feasible placement of bishops and numRemaining is the number of bishops you haven't placed yet.

Then the solution to the problem is

count([], k)

where [] means that no bishops have been placed yet.

The count function can be implemented according to the following pseudocode.

count(currentBishops, numRemaining):
  if numRemaining == 0:
    return 1
  sum = 0
  for each possible board position (x, y):
    if (x, y) does not clash with any bishop in currentBishops:
      let nextBishops be currentBishops augmented with (x, y)
      sum += count(nextBishops, numRemaining-1)
  return sum

In order to avoid an exponential explosion of recursive calls, you'll want to cache the result of each subproblem. This technique is called memoization, and you can implement it as follows.

let memo be a map from (currentBishops, numRemaining) to an integer value

count(currentBishops, numRemaining):
  if numRemaining == 0:
    return 1
  if memo contains (currentBishops, numRemaining):
    return memo[(currentBishops, numRemaining)]
  sum = 0 
  for each possible board position (x, y):
    if (x, y) does not clash with any bishop in currentBishops:
      let nextBishops be currentBishops augmented with (x, y)
      sum += count(nextBishops, numRemaining-1)
  memo[(currentBishops, numRemaining)] = sum
  return sum

The mapping of currentBishops should be one that doesn't care about the order in which you have placed the bishops. You can accomplish this by sorting the bishop positions or making a bitmap of the board when you compute the key for memo.

Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
  • So should currentBishops be an array of bishops? Or an int- and leave current bishops global? ---Also based on your pseudo code it will return two bishops swapped, as two distinct solutions (in other words: let's say we're looking at an example where n=2 and k=2. solution 1 will be (0,0) and (1,0) and solution 2 will be (1,0) and (0,0).) And I want to consider those 1 solution. Therefore I'm considering storing all solutions in a vecor containing a 2d boolean array only marked true in places with a bishop- and then checking through that before adding to sum...but I'm sure there's a btr way – Yigal Saperstein Apr 24 '15 at 20:28
  • On second thought if I put currentbishops as a 2d array that only contains bishops positions than I can confirm old solutions with new ones... Cool idea. That's probably what u meant I ur last sentence – Yigal Saperstein Apr 24 '15 at 20:31
  • I was also thinking about a different type of representation where its a 1d array of int. That represents all the bishops - by a single int where the top left box is 1 bottom left is 4 bottom right is 16 top right is 13. This means value%n will give y and value/n will give x.. – Yigal Saperstein Apr 24 '15 at 20:35
  • You can use the `currentBishops` representation as long as you keep the bishops sorted. That way, the order of placement won't matter. Alternatively, you can store the board as a 2d or 1d array. No matter what representation you choose, think about this: if position (0, 0) is earlier than position (1, 0), you can ensure that once you have placed a bishop at (1, 0), you never consider placing a bishop at (0, 0) again. Do you see how to do that? You can look at the last bishop you placed and start iterating over positions from that position onward. – Michael Laszlo Apr 24 '15 at 21:15
  • i see how... but should currentBishop be a single bishop? or a vector or an array of bishops? and nextBishop. I don't understand what that representation is? I was thinking vector and using pushback- so when you say nextbishop- i'd just pushback another bishop augmented to currentBishop. The problem with this representation is that it will not recurse properly. It will just make a huge vector. – Yigal Saperstein Apr 26 '15 at 03:02
  • `currentBishops` must represent all of the bishops you have placed so far. Perhaps the best representation is the one that is most readily used as a key for `memo`. If the maximum board size is 8 by 8, you can use a 64-bit integer to represent the bishop positions. If boards can be larger, use an array of characters. You can easily turn that into a string for use as a map key. – Michael Laszlo Apr 26 '15 at 03:05
  • If you're using a vector and augmenting it by pushing the new bishop position onto the end, you have to make sure to pop that bishop off the end of the vector after the recursive call has returned. Similarly, if you're using a bitmask, you set the bit before the recursive call and clear the bit after the recursive call. – Michael Laszlo Apr 26 '15 at 03:08
  • I'm a freshman in college, just started programming a few months ago, and don't quite know what a bitmask is... so I figure i'll try the vector thing. so in your pseudo code, ill change the line `sum += count(nextBishops, numRemaining-1)` to `sum += count(currentBishops, numRemaining-1)` and then afterwards add a line of `popback currentBishops`, and set values of a to be same as currentBishops.back() and before i loop through all possible positions, I'll create a new bishop, bishop a, that i will check to see if fits in board, and if so I'll push-back onto the vector. does this sound good? – Yigal Saperstein Apr 26 '15 at 03:16
  • Yes, that's fine. Bear in mind that you don't want to look at board positions that are earlier than the bishop you placed most recently. – Michael Laszlo Apr 26 '15 at 03:24