数据结构与算法[LeetCode]—N_Queen问题
2013-11-03 21:31
417 查看
N-Queen
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.."] ]
深度搜索的基本框架:
DFS(Node)if(Node=目标节点)
then //找到目标,结束
for each next∈d[Node]
do DFS(next);
end
下面贴出两个算法:
第一个简单,但是效率低一点。
用元组x[1:n]表示n后的问题的解。其中,x[i]表示皇后i放在棋盘的第i行的第x[i]列。一行只有一个后,则逐次考虑不同的行则可避免同行问题。由于不允许2个后放在同一列上,所以解向量中的x[i]互不相同。2个皇后不在同一个斜线上是问题的隐约束。假设,从左上角到右下角依次从1-n编号,设主对角线为从左上到右下,在主对角线及平行线上,2个下标值的差(行号-列号)值相等。同理,次对角线为右上角到左下角,2个下标的值的和(行号+列号)相等。因此,若2个皇后分别为(i,j)(k,l)且有i-j=k-l或者i+k=k+l,则说明2个皇后处于同一个斜线上。由此可知,只要|i-k|=|j-l|,就表明连个皇后位于同一条斜线上。
用回朔法解n后问题时,用完全n叉树表示解空间。可行性约束Place剪去不满足行、列和斜线约束的子树。
递归函数backtrack(i)实现对整个解空间的回朔搜索。backtrack(i)搜索解空间中第i层子树。在backtrack()算法中:
(1)当i>.n时,算法搜索至夜节点,得到一个新的n皇后互不攻击的方案,当前已找到的可行方案数sum增1;
(2)当i<=n时,由Place检查其可行性,并以深度优先的方式递归地对可行子树搜索。
本算法只计算了可行解的数目,并未打印所有解方案。
#include<iostream> #include<cmath> using namespace std; class Queen{ friend int nQueen(int); private: bool Place(int k); void Backtrack(int t); int n, //皇后的个数 *x; //当前解x[i] int sum; //当前已找到的可行方案数 }; bool Queen::Place(int k) //判断第K行的x[k]的可行性 { for(int i=1;i<k;i++) if((x[k]==x[i])||abs(i-k)==abs(x[i]-x[k]))return false; return true; } void Queen::Backtrack(int t) { if(t>n)sum++; else for(int i=1;i<=n;i++){ x[t]=i; if(Place(t))Backtrack(t+1); } } int nQueen(int n) { Queen X; //初始化 X.n=n; X.sum=0; int *p=new int [n+1]; for(int i=0;i<=n;i++) p[i]=0; X.x=p; X.Backtrack(1); delete [] p; return X.sum;}
第二个方案相等效率较高。因为它记录了所有先前已经占据的列、占据的主对角线、次对角线。不用每次重新遍历进行绝对值相减计算。那么就要求对主次对角线的编号存储方案有一定的考虑。任何编号方案都可,查询和设置一致即可。本方案打印了出了所有方案,但并未计算总方案数目(容易实现)。本算法参考https://gitcafe.com/soulmachine/LeetCode。
#include<iostream> #include<string> #include<vector> using namespace std; class Solution { public: vector<vector<string> > solveNQueens(int n) { // Note: The Solution object is instantiated only once and is reused by each test case. vector<vector<string> > ret; this->columns=vector<int>(n,0); this->principal_diagonals=vector<int>(2*n,0); this->counter_diagonals=vector<int>(2*n,0); vector<int> C(n,0); //C[i]表示第i行皇后所在列的编号 DFS(0,C,ret); return ret; } private: //三个变量用于剪枝 vector<int> columns; //表示已经放置的皇后占据了哪些列 vector<int> principal_diagonals; //占据了哪些主对角线 vector<int> counter_diagonals; //占据了哪些副对角线 void DFS(int row,vector<int> &C,vector<vector<string> > &ret){ const int N =C.size(); if(row==N){ vector<string> solution; for(int i=0;i<N;++i){ string s(N,'.'); for(int j=0;j<N;++j){ if(j==C[i])s[j]='Q'; } solution.push_back(s); } ret.push_back(solution); return; } for(int j=0;j<N;++j){ //一列一列的验证 坐标为(raw,j) const bool ok=columns[j]==0 && principal_diagonals[j-row+N]==0 && //对角线怎么编号可以自己规划不同的方案但前后一致 counter_diagonals[row+j]==0; if(ok) { //合法,继续递归 C[row]=j; columns[j]=1; principal_diagonals[j-row+N]=1; counter_diagonals[row+j]=1; DFS(row+1,C,ret); //撤销动作 //c[row]=0; //没有必要,总是会继续覆盖,最终会被成功的一组覆盖。 columns[j]=principal_diagonals[j-row+N]=counter_diagonals[row+j]=0; } } } };
当然,还有很多更有优异的方案需要思考。
N-Queens II
Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions.
本题并不要求求出所有解的结果,只需要计算解的总个数。在上一题的基础上,简化了格式化输出的部分代码。只需要用一个全局计数器,当一种方案确定可行时,计数器+1。
class Solution { public: int totalNQueens(int n) { this->col=vector<int>(n,0); this->principal=vector<int>(2*n,0); this->counter=vector<int>(2*n,0); int counts=0; vector<int> C(n,0); //C[i] 表示第i行皇后所在列编号 DFS(0,C,counts); return counts; } private: vector<int> col; //已经放置的皇后,占据了哪些列 vector<int> principal; //已经放置的皇后,占据哪些正对角线 vector<int> counter; //已经放置的皇后,占据了哪些负对角线 void DFS(int row,vector<int> &C,int &counts){ const int N=C.size(); if(row==N){ counts++; return; } for(int j=0;j<N;j++){ //一列列的验证,坐标为(row,j) bool ok=col[j]==0 && principal[j-row+N]==0 && counter[j+row]==0; if(ok){ C[row]=j; col[j]=1; principal[N+j-row]=1; counter[j+row]=1; DFS(row+1,C,counts); //撤销动作 C[row]=0; col[j]=principal[N+j-row]=counter[row+j]=0; } } } };
相关文章推荐
- LeetCode 问题难度,面试出现频率及问题相关数据结构和算法
- LeetCode 问题难度,面试出现频率及问题相关数据结构和算法
- LeetCode 问题难度,面试出现频率及问题相关数据结构和算法
- 数据结构与算法[LeetCode]—两个有序数组合并及找中点问题
- 【数据结构及算法】2.图像染色问题
- 数据结构与算法问题 北大oj 2075(最小生成树)
- 数据结构与算法 leetcode 刷题010
- 【数据结构与算法】约瑟夫环问题
- 数据结构看书笔记(十)—— 求最短路径问题之--迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd)算法
- 【数据结构与算法】链表问题集锦
- 数据结构与算法问题 欧拉回路
- 数据结构与算法学习之路:迷宫问题——回溯思想找出所有路径
- [算法]数据结构算法背包问题解法之递归解法,C语言实现
- 第六周--数据结构之自建算法库之迷宫问题(用队列)
- 数据结构与算法——约瑟夫问题
- PTA 数据结构题目(1):最大子列和问题(分而治之、在线处理算法)
- 数据结构与算法问题 AVL二叉平衡树
- 数据结构与算法--求最大子列和问题
- 【并查集】数据结构与算法实验题 11.2 病毒排查问题
- 数据结构与算法问题 二叉搜索树