N皇后问题的一般解法--回溯法
2015-09-07 19:31
507 查看
先上代码
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
using namespace std;
vector<int> board;
void ShowQueen(void)
{
for(unsigned int i = 0; i < board.size(); i++)
{
cout << "(" << i << "," << board[i] << ") ";
}
cout << endl;
}
bool isValid(int rows, int cols, int order)
{
if(rows >= order || cols >= order)
return false;
for(int i = 0; i < rows; i++)
{
if(cols == board[i])
return false;
else if(abs(cols-board[i]) == abs(rows-i))
return false;
}
return true;
}
int QueenMutex(int k)
{
if(k <= 3)
return 0;
int solutions = 0;
board[0] = 0;
int row = 1;
int col = 0;
while(row != -1)
{
if(isValid(row, col, k))
{
board[row] = col;
if(row == k-1)
{
solutions++;
ShowQueen();
row--;
col = board[row]+1;
}
else
{
row++;
col = 0;
}
}
else
{
col++;
if(col >= k)
{
row--;
col = board[row]+1;
}
}
}
return solutions;
}
int main(int argc, char const *argv[])
{
int order = 4;
if(argc == 2)
{
order = atoi(argv[1]);
if(order <= 0)
{
order = 4;
}
}
board.assign(order, 0);
int n = QueenMutex(order);
cout << "solutions: " << n << endl;
return 0;
}
可以由参数指定要解决问题的规模N,我在ubuntu14.04 64bitOS下模拟出来,一共有92个解,时间10ms左右。
问题需求描述:在一个N×N的二维棋盘内,每行放置一个皇后,使得他们不能相互攻击。
转化:由皇后的走法规则直接将问题直接转化为任意两个皇后不能在同一行或同一列或任意45°角斜线上。
思考的问题1:棋盘和皇后的位置怎么表示?
之前一直想的是用一个N*N的二维数组表示,毕竟这样比较直观。后来发现这样不仅浪费空间,而且也会浪费时间。
一种比较好的办法是:用一个1×N的1维数组表,第i行的元素的值表示该行皇后所在的位置,行与行之间自然分开。
全局容器board用来存放皇后的位置。
思考的问题2:思路
利用穷举法和回溯法,将每一种可能的组合都尝试一边,符合要求的就输出,否则回到上一行皇后的下一个位置开始继续尝试。因此只要写出判断一个皇后的位置是否合法,即可比较清晰地理解思路。该函数为bool isValid(int rows, int cols, int order);若该皇后符合要求,将其位置放到相应的board元素中board[row] = col; 并判断皇后是否已经全部放置好,若是则输出,进而再回到上一行尝试其他组合(line45---50),若不是,则将row加1,进入下一行的判断。若该皇后不符合要求,
将列数加1,继续判断。同时要判断是否到达边界,若是则返回上一行继续尝试(line61---64)。这就是所谓的回溯。本题中由于要找到所有的解,所以不管是找到满足要求的解,还是发现当前组合不能满足时,都要进行回溯。如果只要求找到一种解,则对前一种情况返回即可,而后一种情况仍需回溯。
#include <iostream>
#include <cmath>
#include <vector>
#include <cstdlib>
using namespace std;
vector<int> board;
void ShowQueen(void)
{
for(unsigned int i = 0; i < board.size(); i++)
{
cout << "(" << i << "," << board[i] << ") ";
}
cout << endl;
}
bool isValid(int rows, int cols, int order)
{
if(rows >= order || cols >= order)
return false;
for(int i = 0; i < rows; i++)
{
if(cols == board[i])
return false;
else if(abs(cols-board[i]) == abs(rows-i))
return false;
}
return true;
}
int QueenMutex(int k)
{
if(k <= 3)
return 0;
int solutions = 0;
board[0] = 0;
int row = 1;
int col = 0;
while(row != -1)
{
if(isValid(row, col, k))
{
board[row] = col;
if(row == k-1)
{
solutions++;
ShowQueen();
row--;
col = board[row]+1;
}
else
{
row++;
col = 0;
}
}
else
{
col++;
if(col >= k)
{
row--;
col = board[row]+1;
}
}
}
return solutions;
}
int main(int argc, char const *argv[])
{
int order = 4;
if(argc == 2)
{
order = atoi(argv[1]);
if(order <= 0)
{
order = 4;
}
}
board.assign(order, 0);
int n = QueenMutex(order);
cout << "solutions: " << n << endl;
return 0;
}
可以由参数指定要解决问题的规模N,我在ubuntu14.04 64bitOS下模拟出来,一共有92个解,时间10ms左右。
问题需求描述:在一个N×N的二维棋盘内,每行放置一个皇后,使得他们不能相互攻击。
转化:由皇后的走法规则直接将问题直接转化为任意两个皇后不能在同一行或同一列或任意45°角斜线上。
思考的问题1:棋盘和皇后的位置怎么表示?
之前一直想的是用一个N*N的二维数组表示,毕竟这样比较直观。后来发现这样不仅浪费空间,而且也会浪费时间。
一种比较好的办法是:用一个1×N的1维数组表,第i行的元素的值表示该行皇后所在的位置,行与行之间自然分开。
全局容器board用来存放皇后的位置。
思考的问题2:思路
利用穷举法和回溯法,将每一种可能的组合都尝试一边,符合要求的就输出,否则回到上一行皇后的下一个位置开始继续尝试。因此只要写出判断一个皇后的位置是否合法,即可比较清晰地理解思路。该函数为bool isValid(int rows, int cols, int order);若该皇后符合要求,将其位置放到相应的board元素中board[row] = col; 并判断皇后是否已经全部放置好,若是则输出,进而再回到上一行尝试其他组合(line45---50),若不是,则将row加1,进入下一行的判断。若该皇后不符合要求,
将列数加1,继续判断。同时要判断是否到达边界,若是则返回上一行继续尝试(line61---64)。这就是所谓的回溯。本题中由于要找到所有的解,所以不管是找到满足要求的解,还是发现当前组合不能满足时,都要进行回溯。如果只要求找到一种解,则对前一种情况返回即可,而后一种情况仍需回溯。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法
- C++中调用Lua函数实例
- Lua和C++的通信流程代码实例
- C与C++之间相互调用实例方法讲解
- C++ Custom Control控件向父窗体发送对应的消息
- C++中拷贝构造函数的应用详解