#UVa:11195-Another n-Queen Problem

灆洢 2014-12-25 13:41:31

剛開始直接進行backtracking就TLE了,所以這題要用bitboard的觀念去進行backtracking,底下對bitboard的程式碼進行一下解說:

void initBoard(int board[], int n){
  for( int i = 0 ; i < n ; ++i ){
    board[i] = (1 << n) - 1;
  }
}

這裡是讓可以放置皇后的位置用1來記錄,不可以放置皇后的位置(‘*’)用0來記錄。board這個陣列是以row(y)來當索引值,然後col的部份用bits來記錄。

if( s[j] == '*' ){
  board[i] ^= (1 << j); 
}

這裡就是讓*的位置用1來做XOR運算,簡單來說就是把j位置的1改成0。

backtracking(board, n, 0, (1 << n) - 1, (1 << (2*n-1)) - 1, (1 << (2*n-1)) - 1 )

這邊把記錄x和記錄兩種斜線的bits丟進去backtracking,並且x的n個位數都是1,表示目前都可以擺,而斜線部分的2*n-1個位數也都是1,也表示目前都可以擺。

int nowLeftToRightSlash = leftToRightSlash >> y;
int nowRightToLeftSlash = rightToLeftSlash >> (n-y-1);

將目前跑到那行row的斜線號碼全部抓出來,例如8皇后的第一個row(row[0]),x+y種類的斜線編號分別是0+0=0,0+1=1,0+2=2,0+3=3......0+7=7,也就是說用leftToRightSlash >> 0,這樣即可讓slash1從個位數(2進位)開始的7個位數都剛好對應每一格。

而x-y+(2*n-1)種類的斜線編號分別是0-0+15=15,0-1+15=14........0-7+15=8,正好是從高位數(2進位)數過來0位數,那為了推到個位數(2進位),就變成rightToLeftSlash >> 8-0-1,這樣就可以讓高位數(2進位)的7個位數推到個位數(2進位)來。

int canPutQueen = board[y] & x & nowLeftToRightSlash & nowRightToLeftSlash;

用&看看每一格是否能夠放置皇后,board[]檢查是否為*,x檢查是否這個col被放置皇后,nowLeftToRightSlash檢查是否這條斜線被放置皇后,nowRightToLeftSlash檢查另外一種斜線是否被放置皇后,如果全部都是1,就表示這格可以放置皇后。

while( canPutQueen != 0 ){
  int xPut = canPutQueen & (-canPutQueen);
  numberOfSolution += backtracking( board, n, y+1, x ^ xPut, leftToRightSlash ^ (xPut << y), rightToLeftSlash ^ (xPut << (n-y-1)) );
  canPutQueen ^= xPut;
}

第一行自己&負自己,可以得到二進位最低位的1,而我就從這最低位的1開始放置皇后,那麼x就要記錄放置皇后,而leftToRightSlash和rightToLeftSlash也要記錄放置了皇后,最後再將這個1從canPutQueen裡面去除,再繼續把皇后放在下一個1的位置。

C++(0.978)

/*******************************************************/
/* UVa 11195 Another n-Queen Problem                   */
/* Author: Maplewing [at] knightzone.studio            */
/* Version: 2014/12/25                                 */
/*******************************************************/
#include <iostream>
#include <cstdio>
#include <string>
using namespace std;

void initBoard(int board[], int n){
  for( int i = 0 ; i < n ; ++i ){
    board[i] = (1 << n) - 1;
  }
}

int backtracking(int board[], int n, int y, int x, int leftToRightSlash, int rightToLeftSlash ){
  if( y == n ){
    return 1;
  }

  int nowLeftToRightSlash = leftToRightSlash >> y;
  int nowRightToLeftSlash = rightToLeftSlash >> (n-y-1);
  int canPutQueen = board[y] & x & nowLeftToRightSlash & nowRightToLeftSlash;

  int numberOfSolution = 0;
  while( canPutQueen != 0 ){
    int xPut = canPutQueen & (-canPutQueen);
    numberOfSolution += backtracking( board, n, y+1, x ^ xPut, leftToRightSlash ^ (xPut << y), rightToLeftSlash ^ (xPut << (n-y-1)) );
    canPutQueen ^= xPut;
  }

  return numberOfSolution;
}

int main(){
  int n;
  int casenumber = 1;
  while( scanf("%d", &n) != EOF && n != 0 ){
    int board[20];
    initBoard( board, n );

    string s;
    getline(cin, s); // Delete \n
    for( int i = 0 ; i < n ; ++i ){
      getline( cin, s );

      for( int j = 0 ; j < n ; ++j ){
        if( s[j] == '*' ){
          board[i] ^= (1 << j); 
        }
      }
    }

    printf("Case %d: %d\n", casenumber++, backtracking(board, n, 0, (1 << n) - 1, (1 << (2*n-1)) - 1, (1 << (2*n-1)) - 1 ) );
  }

  return 0;
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料