关键:回溯法
方法一:用二维数组代表棋盘,皇后在的格子为true,其它为false。从第一行开始,放入一个皇后,然后到第二行,检查合法的格子,放入第二个皇后,依次下去,直到某一行没有合法的格子放下皇后,然后撤回上一次放入的皇后,重新寻找合法的格子以放下该皇后。重复上面的步骤。直到皇后全部摆好为止。
代码:
#ifndef QUEENS_H
#define QUEENS_H
#include<iostream>
using namespace std;
const int maxSize = 32;
class Queens
{
public:
Queens(int size)
{
this->size = size;
count = 0;
resultNumber = 0;
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
{
board[i][j] = false;
}
}
bool solved() const
{
return size == count;
}
bool unguarded(int col) const
{
bool ok = true;
int i;
for (i = 0; ok && i < count; ++i)
{
if (board[i][col] == true)
{
ok = !board[i][col];
return ok;
}
}
for (i = 1; ok && i <= count && i <= col; ++i)
{
if (board[count - i][col - i] == true)
{
ok = !board[count - i][col - i];
return ok;
}
}
for (i = 1; ok && i <= count && i < size - col; ++i)
{
if (board[count - i][col + i] == true)
{
ok = !board[count - i][col + i];
return ok;
}
}
return ok;
}
void insert(int col)
{
board[count++][col] = true;
}
void remove(int col)
{
board[--count][col] = false;
}
void print()
{
resultNumber++;
cout << resultNumber << endl;
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
{
cout << board[i][j];
if (j == size - 1)
cout << endl;
}
}
int getSize() { return size; };
private:
int size;
int count;
long resultNumber;
bool board[maxSize][maxSize];
};
#endif // !QUEENS_H
void solve(Queens &config)
{
if (config.solved())
config.print();
else
{
for (int j = 0; j < config.getSize(); ++j)
{
if (config.unguarded(j))
{
config.insert(j);
solve(config);
config.remove(j);
}
}
}
}
int main()
{
int numberOfQueens;
cout << "input the number of queens, no more than 32\n ";
cin >> numberOfQueens;
Queens config(numberOfQueens);
solve(config);
return 0;
}
分析:方法一中,大量的时间浪费在检查格子的合法性上面,因为该函数存在循环。可以通过改变二维数组的数据,使其存储被控制的次数,如2表示两个皇后控制它,0表示它是可以放皇后的。回溯时,将其值减一。这样改进,可以减小复杂度,但是在插入皇后时,还是需要循环来设置它所控制的格子。还可以改进,完全消去循环。使用原本的定义,找出空闲的列,和斜线。斜线分上斜线与下斜线。每条上斜线上的格子,行加列为固定值,每条下斜线的格子,行减列为固定值。因此不再是检查空闲的格子,而是检查空闲的列和斜线,这可以根据传来的参数(列)立即确定,插入时也只是写入对列和斜线的控制,从而消去循环。
代码:
#ifndef QUEENS2_H
#define QUEENS2_H
#include<iostream>
using namespace std;
const int maxSize = 32;
class Queens2
{
public:
Queens2(int size)
{
this->size = size;
count = 0;
resultNumber = 0;
for (int i = 0; i < maxSize; ++i) colFree[i] = true;
for (int i = 0; i < 2 * maxSize - 1; ++i) downFree[i] = true;
for (int i = 0; i < 2 * maxSize - 1; ++i) upFree[i] = true;
}
bool solved() const
{
return size == count;
}
bool unguarded(int col) const
{
return colFree[col] && upFree[count + col] && downFree[count - col + size - 1];
}
void insert(int col)
{
queenInRow[count] = col;
colFree[col] = false;
upFree[count + col] = false;
downFree[count - col + size - 1] = false;
++count;
}
void remove(int col)
{
--count;
colFree[col] = true;
upFree[count + col] = true;
downFree[count - col + size - 1] = true;
}
void print()
{
resultNumber++;
cout << resultNumber << endl;
for (int i = 0; i < size; ++i)
for (int j = 0; j < size; ++j)
{
if (j == queenInRow[i])
cout << '1';
else
cout << '0';
if (j == size - 1)
cout << endl;
}
}
int getSize() { return size; };
private:
int size;
int count;
long resultNumber;
int queenInRow[maxSize];
bool colFree[maxSize];
bool downFree[2 * maxSize - 1];
bool upFree[2 * maxSize - 1];
};
#endif // !QUEENS_H
void solve(Queens2 &config)
{
if (config.solved())
config.print();
else
{
for (int j = 0; j < config.getSize(); ++j)
{
if (config.unguarded(j))
{
config.insert(j);
solve(config);
config.remove(j);
}
}
}
}
int main()
{
int numberOfQueens;
cout << "input the number of queens, no more than 32\n ";
cin >> numberOfQueens;
Queens2 config(numberOfQueens);
solve(config);
return 0;
}
经过改进,程序所需要的步骤已经接近回溯中探查的步骤数。然而皇后问题复杂度比指数还大,这是算法的本质所致。也是该问题的本质使然。