问题描述:

  在n*n的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n*n格的棋盘上放置n个皇后,任何2个皇后不放在同一行或同一列或同一斜线。

盲目的迭代枚举:

 /*
*作者:xymaqingxiang
*说明:八皇后——盲目迭代法
*分析:通过8重循环模拟搜索空间中的8^8个状态,从中找出满足约束条件的可行性方案
*日期:2014-05-15
*/
#include <iostream>
#include <cstdlib>
using namespace std;
bool place(int a[],int n)
{
for(int i=;i<=n;i++)
{
for(int j=;j<=i-;j++)
{
if ((a[i]==a[j])||(abs(a[i]-a[j])==i-j))//不同列和不同斜线约束
{
return false;//位置冲突
}
}
}
return true;//不冲突
} void queens()
{
int a[];
int count = ;
//每个皇后都从第一列位置开始判断
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
if(!place(a,)) //判断位置的合法性
continue;
else
{
for(int i=;i<=;i++)
{
cout<<a[i]; //打印满足约束的可行性放置方案
}
cout<<endl;
count++;
}
}
}
}
}
}
} }
}
cout<<count<<endl;
} void main()
{
queens();
system("pause");
}

盲目枚举——nQueen

 /*
*作者:xymaqingxiang
*说明:八皇后——盲目迭代法
*分析:通过8重循环模拟搜索空间中的8^8个状态,从中找出满足约束条件的可行性方案,但是这样会走很多冤枉路,而回溯的思想就是——走不通则掉头
所以在下面的算法实现中,每进行一个皇后位置的讨论就立刻进行位置的检查,不满足则结束本路径,回头走下一路径,这样便能减少很多冤枉路。
本算法虽可读性良好,但仍局限于解决8皇后问题,对于解决任何N皇后问题还要修改程序结构,不能把皇后个数n作为参数传递给函数,不具有普遍性。
而且程序中出现了大量的for循环,而且for循环的结构很相似,自然让我们想到迭代回溯,即下面我们要讨论的递归回溯和非递归回溯
*日期:2014-05-15
*/
#include <iostream>
#include <cstdlib>
using namespace std;
bool place(int a[ ],int n)
{//多次被调用,只需一重循环
for(int i=;i<=n-;i++)
{
if((abs(a[i]-a[n])==n-i)||(a[i]==a[n]))
return false;
}
return true;
} void queens()
{
int a[];
int count = ;
for(a[]=;a[]<=;a[]++)
{
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,)) continue;
for(a[]=;a[]<=;a[]++)
{
if (!place(a,))
continue;
else
{
for(int i=;i<=;i++)
{
cout<<a[i];
}
cout<<endl;
count++;
}
}
}
}
}
}
} }
}
cout<<count<<endl;
} void main()
{
queens();
system("pause");
}

改进版盲目枚举——nQueen

  以上两种算法虽可读性良好,但仍局限于解决8皇后问题,对于解决任何N皇后问题还要修改程序结构,不能把皇后个数n作为参数传递给函数,不具有普遍性。而且程序中出现了大量的for循环,而且for循环的结构很相似,自然让我们想到迭代回溯,即下面我们要讨论的递归回溯和非递归回溯。

算法分析:

  用n元组x[1:n]表示n后问题的解,其中x[i]表示皇后 i 放在棋盘的第 i 行的第x[i]列

  不同行:事先规定好 i 号皇后只能放置在 i 行上,这样就解决了同行的问题。

  不同列:解向量中的x[i]互不相同,即x[i]!=x[j],如此便实现了不同列的限制。

  不同斜线:最终任意两个皇后的位置(i,x[i])和(j,x[j])不在同一斜线,即斜率的绝对值不能为1,也就是|j-i|!=|x[j]-x[i]|。

  用回溯法解决n后问题时,用完全n叉树表示解空间,可行性约束Place减去不满足行、列和斜线约束的子树。

  下面具体解n后问题的回溯法中,递归函数Backtrack(1)实现对整个解空间的回溯搜索。Backtrack(i)搜索解空间中的第 i 层子树。类Queen的数据成员记录解空间结点信息,以减少传给Backtrack的参数。sum记录当前已找到的可行方案数。

  在算法Backtrack中:

    1、 当i>n时,算法搜索至叶节点,得到一个新的n皇后互不攻击放置方案,当前已找到的可行方案数sum加1。

    2 、当i<=n时,当前扩展结点Z是解空间中的内部结点。该结点有x[i]=1,2,3....n共n个儿子结点。对当前扩展结点Z的每一个儿子结点,由Place检察其可行性,并以深度优先的方式递归地对可行子树搜索,或剪去不可行子树。

算法描述1:————子集树(仅仅隐藏掉行约束)——规定好行位置,列位置从第一列开始讨论

 #include <iostream>
#include <cstdlib>
using namespace std;
class Queen{
friend int nQueen(int);
private:
bool Place(int k);
void Backtrack(int t);
int n,    //皇后个数
* x;   //当前解空间
long sum;   //当前已找到的可行方案数
};
bool Queen::Place(int k)  //位置检查,满足约束则返回true,否则返回false
{
for(int j=;j<k;j++)    //检查k个皇后不同列和不同斜线的约束语句
if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))
return false;
return true;
}
void Queen::Backtrack(int t)
{
if(t>n)
sum++;  //搜索至叶结点,即讨论完最后一个皇后的位置,得到一个新的不受攻击放置方案,可行方案数加 1
else
for(int i=;i<=n;i++)
{
x[t] = i;        //确定第 i 个皇后的列位置
if(Place(t))      //检查放置位置是否满足约束条件
Backtrack(t+);  //深度优先搜索可行子树
}
}
int nQueen(int n)
{
Queen X;  //定义并初始化X的信息
X.n = n;
X.sum = ;
int *p = new int [n+];
for(int i=;i<=n;i++)
p[i] = ;
X.x = p;
X.Backtrack();
delete [] p;
cout<<X.sum<<endl;
return X.sum;
}
int main()
{
nQueen();
nQueen();
nQueen();
return ;
}

迭代回溯:

  数组x 记录了解空间树中从根到当前扩展结点的路径,这些信息已包含了回溯法在回溯时所需要的信息。利用数组x 所含的信息,可将上述回溯法表示成非递归形式,进一步省去O(n)递归栈空间。

具体的算法描述为:

 #include <iostream>
#include <cstdlib>
using namespace std;
class Queen{
friend int nQueen(int);
private:
bool Place(int k);
void Backtrack(void);//.........
int n,
* x;
long sum;
};
bool Queen::Place(int k)  //位置检查函数,满足约束返回true,不满足返回false
{
for(int j=;j<k;j++)
if( ( abs(k-j) == abs(x[j]-x[k]) ) ||( x[j] == x[k] ) )
return false;
return true;
}
void Queen::Backtrack(void)//非递归实现
{
x[] = ;  //x[1]赋初值为0
int k = ;  //从第一个皇后开始讨论
while(k>)
{
x[k]+=;  //x[1]赋初值为0,加1后,表示首先从第一列开始放
while( (x[k]<=n) && !(Place(k)) )//k还不是最后的叶子结点,且位置没有冲突,满足Place函数的约束————x[k]代表列的位置,没有超出列的限制
x[k] += ;  //在没有超出列的限制且不满足Place函数位置约束时,列后移
if(x[k] <= n)  //没有超出列的限制范围
if(k == n) //k是叶子结点,已经讨论到最后一个皇后
sum++;  //可行方案数加1
else
{  //未到达叶子节点(最后一个皇后),且已经为当前的皇后找到合适的位置,k++后处理下一个皇后的放置位置
k++;    //考虑下一个皇后的放置位置
x[k] = ;  //初始位置之前赋值为0
}
else
k--;  //由于已经超出列的限制范围,且当前正考虑的皇后没有找到合适的位置,则前一个皇后的位置后移,重新来过
}
}
int nQueen(int n)
{
Queen X;
X.n = n;
X.sum = ;
int *p = new int [n+];
for(int i=;i<=n;i++)
p[i] = ;
X.x = p;
X.Backtrack();//......
delete [] p;
cout<<X.sum<<endl;
return X.sum;
}
int main()
{
nQueen();
nQueen();
nQueen();
return ;
}

算法描述2:————排列树(隐藏掉行和列约束)——>规定好行位置的同时,列位置从j(不同于上一个皇后的列)开始讨论

 #include <iostream>
#include <cstdlib>
using namespace std;
class Queen{
friend int nQueen(int);
private:
bool Place(int k);
void swap(int t,int i,int *x);
void Backtrack(int t);
int n, //皇后个数
* x;//当前解空间
long sum;//当前已找到的可行方案数
};
bool Queen::Place(int k)//位置检查,满足约束则返回true,否则返回false
{
for(int j=;j<k;j++)//检查k个皇后不同列和不同斜线的约束语句
if((abs(k-j)==abs(x[j]-x[k]))||(x[j]==x[k]))
return false;
return true;
}
void Queen::swap(int t,int i,int *x)
{
int k;
k=x[t];
x[t]=x[i];
x[i]=k;
} void Queen::Backtrack(int t)
{
if(t>n)
{
for(int i=;i<=n;i++)  //方案数加1前,先打印符合要求的排列组合
cout<<x[i];
cout<<endl;
sum++;//搜索至叶结点,即讨论完最后一个皇后的位置,得到一个新的不受攻击放置方案,可行方案数加 1
}
else
for(int i=t;i<=n;i++)//控制分支数目,每次都要减一,初始值从t开始而非1
{
swap(t,i,x);//交换位置,得到一种新的排列————轮岗ing
if(Place(t))//检查放置位置是否满足约束条件
Backtrack(t+);//深度优先搜索可行子树
swap(t,i,x);//调回原位置(初始排列组合)———— 众神归位
}
}
int nQueen(int n)
{
Queen X;//定义并初始化X的信息
X.n = n;
X.sum = ;
int *p = new int [n+];
for(int i=;i<=n;i++)
p[i] = i;  //给x[i]赋初值,必须是其排列的一种
X.x = p;
X.Backtrack();
cout<<X.sum<<endl;
delete [] p;
return X.sum;
}
int main()
{
nQueen();
nQueen();
nQueen();
system("pause");
return ;
}

回溯法——n后问题的更多相关文章

  1. 回溯法解决N皇后问题(以四皇后为例)

    以4皇后为例,其他的N皇后问题以此类推.所谓4皇后问题就是求解如何在4×4的棋盘上无冲突的摆放4个皇后棋子.在国际象棋中,皇后的移动方式为横竖交叉的,因此在任意一个皇后所在位置的水平.竖直.以及45度 ...

  2. leetcode_401_Binary Watch_回溯法_java实现

    题目: A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bot ...

  3. N-Queens And N-Queens II [LeetCode] + Generate Parentheses[LeetCode] + 回溯法

    回溯法 百度百科:回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步又一次选择,这样的走不通就退回再走的技术为回溯法 ...

  4. 回溯法、数独与N阶可达问题

    回溯法是剪了枝的穷举,这是字面上的说法,不太好理解,不如讲解实例来的酸爽,于是引出了N阶可达问题: 有N个国家,每个国家有若干城市,小明要从中国(任意一个城市)出发,遍历所有国家(假设这个遍历顺序已经 ...

  5. C语言递归回溯法迷宫求解

    本例将随机产生一个10*10的迷宫输出后,在下面输出此迷宫的解法. 解法为从坐标(1,1)处进入,从(8,8,)出去,优先线路为先右后下再上最后为左. 不少人求解此题时运用的栈的相关知识,本例寻找线路 ...

  6. 从Leetcode的Combination Sum系列谈起回溯法

    在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...

  7. 算法——八皇后问题(eight queen puzzle)之回溯法求解

    八皇后谜题是经典的一个问题,其解法一共有种! 其定义: 首先定义一个8*8的棋盘 我们有八个皇后在手里,目的是把八个都放在棋盘中 位于皇后的水平和垂直方向的棋格不能有其他皇后 位于皇后的斜对角线上的棋 ...

  8. js实现八皇后,回溯法

    八皇后问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法? 两个皇后不能同时在同一行,同一列,和斜对角线的位置上,使用回溯法解决. 从第一行选个位置开始放棋 ...

  9. P1378 油滴扩展 dfs回溯法

    题目描述 在一个长方形框子里,最多有N(0≤N≤6)个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界.必须等一个油滴扩展完毕才能放置下一个油滴. ...

随机推荐

  1. Office如何加密解密

    1 任意打开一个EXCEL文档,并点击工具,选项,切换到安全性选项卡,任意设置密码   设置密码保护之后再次打开就需要输入密码 我们使用该软件Office PassWord Remover打开该文档( ...

  2. SqlServer--百度百科

    SQL是英文Structured Query Language的缩写,意思为结构化查询语言.SQL语言的主要功能就是同各种数据库建立联系,进行沟通.按照ANSI(美国国家标准协会)的规定,SQL被作为 ...

  3. lodash kebabCase

    _.kebabCase([string='']) 转换字符串为 kebab case. _.kebabCase('Foo Bar'); // => 'foo-bar' _.kebabCase(' ...

  4. [linux]w命令和uptime命令查看系统负载

    在Linux系统中查询系统CPU和内存的负载(使用率)时,我们通常习惯于使用top.atop或者ps,这篇文章将要给大家介绍如何使用w命令和uptime命令来查看系统的负载情况,对于uptime命令, ...

  5. atitit.MIZIAN 陕北方言 特有词汇 大词典 attilax 整理 h--n v1 q31.xlsx

    atitit.MIZIAN 陕北方言 特有词汇 大词典 attilax 整理 h--n v1 q31.xlsx 1  Mizian陕北方言 english英语 spain西班牙语 cantonese粤 ...

  6. First MFC

    // stdafx.h : include file for standard system include files, // or project specific include files t ...

  7. dubbo_实现Hessian的远程调用协议

    1.优点 连接个数:多连接 连接方式:短连接 传输协议:HTTP 传输方式:同步传输 序列化:Hessian二进制序列化 适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文 ...

  8. Creating Icon Overlay Handlers / 创建图标标记 Handlers (续) / VC++, Windows, DLL, ATL, COM

    创建图标标记 Handlers (续) 1.新建一个ATL Project. 2.建议将 Project Property 中 Linker – General - “Register Output” ...

  9. java 中 HashMap 遍历与删除

    HashMap的遍历 方法一.这是最常见的并且在大多数情况下也是最可取的遍历方式 /** * 在键值都需要时使用 */ Map<Integer, Integer> map = new Ha ...

  10. 62. Search in Rotated Sorted Array【medium】

    62. Search in Rotated Sorted Array[medium] Suppose a sorted array is rotated at some pivot unknown t ...