回溯法——n后问题
问题描述:
在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后问题的更多相关文章
- 回溯法解决N皇后问题(以四皇后为例)
以4皇后为例,其他的N皇后问题以此类推.所谓4皇后问题就是求解如何在4×4的棋盘上无冲突的摆放4个皇后棋子.在国际象棋中,皇后的移动方式为横竖交叉的,因此在任意一个皇后所在位置的水平.竖直.以及45度 ...
- 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 ...
- N-Queens And N-Queens II [LeetCode] + Generate Parentheses[LeetCode] + 回溯法
回溯法 百度百科:回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步又一次选择,这样的走不通就退回再走的技术为回溯法 ...
- 回溯法、数独与N阶可达问题
回溯法是剪了枝的穷举,这是字面上的说法,不太好理解,不如讲解实例来的酸爽,于是引出了N阶可达问题: 有N个国家,每个国家有若干城市,小明要从中国(任意一个城市)出发,遍历所有国家(假设这个遍历顺序已经 ...
- C语言递归回溯法迷宫求解
本例将随机产生一个10*10的迷宫输出后,在下面输出此迷宫的解法. 解法为从坐标(1,1)处进入,从(8,8,)出去,优先线路为先右后下再上最后为左. 不少人求解此题时运用的栈的相关知识,本例寻找线路 ...
- 从Leetcode的Combination Sum系列谈起回溯法
在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...
- 算法——八皇后问题(eight queen puzzle)之回溯法求解
八皇后谜题是经典的一个问题,其解法一共有种! 其定义: 首先定义一个8*8的棋盘 我们有八个皇后在手里,目的是把八个都放在棋盘中 位于皇后的水平和垂直方向的棋格不能有其他皇后 位于皇后的斜对角线上的棋 ...
- js实现八皇后,回溯法
八皇后问题:将八个皇后摆在一张8*8的国际象棋棋盘上,使每个皇后都无法吃掉别的皇后,一共有多少种摆法? 两个皇后不能同时在同一行,同一列,和斜对角线的位置上,使用回溯法解决. 从第一行选个位置开始放棋 ...
- P1378 油滴扩展 dfs回溯法
题目描述 在一个长方形框子里,最多有N(0≤N≤6)个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界.必须等一个油滴扩展完毕才能放置下一个油滴. ...
随机推荐
- TestNG简单的学习-TestNG运行
转载:http://topmanopensource.iteye.com/blog/1983735 TestNG简单的学习-TestNG运行 文档来自官方地址: http://testng.org/d ...
- FBX导入错误 :ImportFBX Errors:
原地址:http://www.cnblogs.com/lopezycj/archive/2012/05/16/unity3d_tuchao.html Unity3D吐槽1--FBX导入 Unity3d ...
- redis源代码分析(5)——aof
前面几篇基本介绍了redis的主要功能.流程.接下来是一些相对独立的部分,首先看一下持久化. redis持久化支持两种方式:RDB和AOF,我们首先看一下AOF的实现. AOF(Append only ...
- hibernate 悲观锁乐观锁
悲观锁和乐观锁是:在事务隔离机制中设置了ReadCommited的情况下,两种可以避免不可重复读的方式. 设置成读已提交是考虑到安全和处理速度,保证并发效率,但是在这个情况下仍然需要避免不可重复读 ...
- 使用CXF实现基于Soap协议的WebService
本文介绍使用CXF实现基于Soap协议的WebService(CXF的版本是3.0.0) 一. 前言 Java有三种WebService规范:Jax-WS,Jax-RS,Jaxm 1. Jax-WS( ...
- Cocos2dx报OpenGL error 0x0506错误
近期做第三方sdk接入时,发现iOS8系统下,进行银联充值后,返回游戏有很大几率会报 OpenGL error 0x0506............ 之类的绘制问题,游戏卡死,花了很长时间,一直没有头 ...
- Cocos2d-x 3.x版2048游戏开发
Cocos2d-x 3.x版2048游戏开发 本篇博客给大家介绍怎样高速开发2048这样一款休闲游戏,理解整个2048游戏的开发流程.从本篇博客你将能够学习到下面内容: 这里注明一下,本教程来自极客学 ...
- python判断文件是否存在目录中
##支持排除文件和目录#!/usr/bin/python #coding:utf-8 import os def list_file(file_name): data = [] file_name = ...
- MongoDB安装、CURD增改查删操作、应用场景
NoSQL(NoSQL = Not Only SQL ),意即"不仅仅是SQL".非关系型的数据存储 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 ...
- 用户输入URL后发生了什么,以及优化问题
用户角度:1.打开浏览器2.输入URL3.按下回车4.浏览器呈现画面 当用户输入页面地址后,浏览器获得用户希望访问的地址,便向该站点服务器发起一系列的请求,这些请求不光包括对页面的请求,还包括对页面中 ...