回溯算法——算法总结(四)

回溯算法也叫试探法,它是一种系统地搜索问题的解的方法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。用回溯算法解决这个问题的一般步骤为:

1、定义一个解空间。它包括问题的解。

2、利用适于搜索的方法组织解空间。

3、利用深度优先法搜索解空间。

4、利用限界函数避免移动到不可能产生解的子空间。

问题的解空间一般是在搜索问题的解的过程中动态产生的。这是回溯算法的一个重要特性。

经典样例(N后问题):

要求在一个n*n格的棋盘上放置n个皇后,使得她们彼此不受攻击,即使得不论什么两个皇后不能被放在同一行或同一列或同一斜线上(国际象棋中一个皇后能够攻击与之处在同一行或同一列或同一斜线上的其它不论什么棋子)。

注意一般会多种可行的放置方案。

n皇后问题是NP全然的。用回溯法求解:

(1)定义问题的解空间:用n元组x[1:n]表示它的解。即一个可行的放置方案。当中x[i]表示皇后i放在棋盘的第i行第x[i]列。因为不同意将不论什么两个皇后放在同一列。所以解向量中诸x[i]互不同样。而不论什么两个皇后不能放在同一斜线上是问题的隐约束。在搜索时,可通过这个斜线约束来剪去无效的子树。将棋盘看作是二维方阵,从左上角到右下角的主对角线及其平行线上(即斜率为-1的各斜线),

元素的两个下标值的差值相等。

同理,斜率为+1的每一条斜线上,元素的两个下标值的和相等。

因此,若两个皇后放置位置为(i,j)和(k,l),且i-j=k-l或i+j=k+l,则这两个皇后处于同一斜线上。这里两个等式可统一表示为|i-k|=|j-l|。

(2)确定解空间树的结构:能够用一棵全然n叉树来表示n皇后问题的解空间。

树枝上用放置的列号来标记,从树根到任一叶子结点的路径都是一种放置方案。这个全然n叉树共同拥有n**n(即n的n次方)个叶子结点,因此遍历解空间树的算法须要O(n**n)的时间复杂度。

(3)以深度优先方式搜索整个解空间,找出所要的解:约束函数为Plack(k)。它用来測试将皇后k放在x[k]列是否与前面已放置的k-1个皇后都不在同一列。且都不在同一斜线上。在回溯函数中。若搜索到达叶子结点(i>n)。则得到一个新的n皇后互不攻击的可行方案,可行方案数加1。

若还未到达叶子结点(i<=n),因为是n叉树。当前扩展结点有n个放置列号x[i]=1,2,...,n所表示的n个儿子结点,对皇后i的每一种列号放置情况,用结束函数Place检查其可行性,若可行则进入这个子树进行递归搜索,否则剪去这个子树。

算法实现例如以下:

//问题及其解空间描写叙述
 class Queen{
private:
    friend int nQueen(int);  //算法实现函数
    bool Place(int k);  //约束函数
    void Backtrack(int i); //回溯搜索函数
    int n;  //皇后个数
    int* x;  //当前解
    long sum;  //当前已找到的可行方案数
 };  

 //约束函数
 bool Queen::Place(int k){
    for(int j=1;j<k;j++)  //推断若将皇后k放在(k,x[k])处,与前面的每个皇后(j,x[j])是否都不在同一列,且都不在同一斜线上
        if( (x[j]==x[k]) || (abs(k-j)==abs(x[j]-x[k])) )
            return false;
    return true;
 }  

 void Queen::Backtrack(int i){
    if(i>n) //搜索到达叶子结点,得到一个新的可行的放置方案
        sum++;
    else  //否则没达到叶子结点。当前扩展结点有n个列号x[i]=1,2,...,n表示的n个儿子结点
        for(int j=1;j<=n;j++){
            x[i]=j;  //将皇后i放置在第j列
            if(Place(i)) //若这个放置可行,则进入这个子树进行递归搜索
                Backtrack(i+1);
        }
 }  

 //算法实现函数:返回可行的放置方案个数,这里数组x的长度应该为n+1。
 //x[1]~x[n]存放了当中的一个可行放置方案
 int nQueen(int* x,int n){
    Queen alg;
    alg.n=n;
    alg.sum=0;
    for(int i=1;i<=n;i++)  //初始化各皇后的放置情况
        alg.x[i]=0;
    alg.Backtrack(1);  //搜索整个解空间树
    delete[] alg.x;
    return alg.sum;
 }  

由于解空间树有n**n(即n的n次方)个叶子结点,因此算法的时间复杂度为O(n**n),非常耗时。回溯法有通用解题法之称。由于它採用的是搜索整个解空间的策略。对一些无从下手的、组合数较大的问题(特别是非常多NP全然问题),回溯法是一个有力的工具。回溯法对解空间进行了有效的组织(组织成树或图的结构)。还能够用剪枝函数来提高平均搜索效率,因此它的效率大大高于纯粹的穷举法。

      小结:

考试尽管已经过去。偶尔回首看看,还是有非常多收获的 。 回顾着写这些博客。可能有错误,也可能有一些理解不到的地方。如有问题。欢迎指正!

时间: 2024-10-27 07:19:15

回溯算法——算法总结(四)的相关文章

这是递归和回溯的算法 是abcd&#39;的排列可能

#include<stdio.h>#include<iomanip>#include<iostream>using namespace std;bool b[10]={0};int a[10]={0};int print(){ for (int i=1;i<=4;i++) printf("%c ",a[i]); printf("\n");} int dosomething(int z){ int mm; for ( mm=1

算法(第四版)C#题解&mdash;&mdash;1.3.49 用 6 个栈实现一个 O(1) 队列

因为这个解法有点复杂,因此单独开一贴介绍. <算法(第四版)>中的题目是这样的: 1.3.49 栈与队列.用有限个栈实现一个队列,保证每个队列操作(在最坏情况下)都只需要常数次的栈操作. 那么这里就使用六个栈来解决这个问题. 这个算法来自于这篇论文. 原文里用的是 Pure Lisp,不过语法很简单,还是很容易看懂的. 先导知识--用两个栈模拟一个队列 如何使用两个栈来模拟一个队列操作? 这是一道很经典的题目,答案也有很多种,这里只介绍之后会用到的一种方法. 首先我们有两个栈,H 和 T,分别

每日算法之三十四:Multiply Strings

大数相乘,分别都是用字符串表示的两个大数,求相乘之后的结果表示. 首先我们应该考虑一下测试用例会有哪些,先准备测试用例对防御性编程会有比较大的帮助,能够考虑一些极端情况.有以下几种用例: 1)"0","0" 2)"0","879127346783" 其中一个是零 3)"as234","123343"  存在非法字符 4)"000000000000001234",&qu

浅谈算法和数据结构: 四 快速排序

原文:浅谈算法和数据结构: 四 快速排序 上篇文章介绍了时间复杂度为O(nlgn)的合并排序,本篇文章介绍时间复杂度同样为O(nlgn)但是排序速度比合并排序更快的快速排序(Quick Sort). 快速排序是20世纪科技领域的十大算法之一 ,他由C. A. R. Hoare于1960年提出的一种划分交换排序. 快速排序也是一种采用分治法解决问题的一个典型应用.在很多编程语言中,对数组,列表进行的非稳定排序在内部实现中都使用的是快速排序.而且快速排序在面试中经常会遇到. 本文首先介绍快速排序的思

javascript 回溯寻路算法

最近一直在手游 caveboy escape(安卓上,不知道IOS上有没有,可以下来玩玩). 游戏规则是,在5x5的矩阵,从最下面的起点,每个颜色走三步,到达最上面的重点. 想写个js版本.碰到第一个问题就是,矩阵布局,寻路算法. 网上搜了下只有 PathFinding.js 带有的著名的 A*寻路法(自己百度) 源码: https://github.com/qiao/PathFinding.js DEMO: http://qiao.github.io/PathFinding.js/visual

ubuntu命令行下java工程编辑与算法(第四版)环境配置

ubuntu命令行下java工程编辑与算法(第四版)环境配置 java 命令行 javac java 在学习算法(第四版)中的实例时,因需要安装配套的java编译环境,可是在编译java文件的时候总是出各种错误,特在此总结一下. ubuntu下java环境配置 由于网上教程比较多,而且也较全面,特此摆放一个链接,跟着此教程总就可以配置好oracle的java jdk,如果想更加省事,直接在命令行下键入java,会提示安装各种开源java jdk,只需要一个命令即可: sudo apt-get i

算法系列(四)排序算法下篇--如何超越排序算法下界

概述 在算法系列(四)排序算法中篇--归并排序和快速排序一文中,我们介绍了归并排序和快速排序,最坏的情况下,最快的排序算法的时间复杂度是O(nlogn),是否有更好的算法呢?到目前为止,没有特殊的规则,O(nlogn)已经是最好的排序算法了,也就是说通用排序算法的时间复杂度下界就是O(nlogn).如果限定一些规则,是可以打破这个下界的.下面说一下尽在O(n)时间内就能实现对数组排序的算法. 基于排序的规则 基于什么样的规则才能突破排序的下界呢?我们需要分析一下排序消耗的时间.排序需要遍历,比较

每日算法之十四:3Sum

Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Note: Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c) The solut

数据挖掘算法学习(四)PCA算法

算法简介 主成分分析(PrincipalComponentAnalysis,简称PCA)是一种常用的基于变量协方差矩阵对信息进行处理.压缩和抽提的有效方法.主要用于对特征进行降维. 算法假设 数据的概率分布满足高斯分布或是指数型的概率分布.方差高的向量视为主元. 算法输入 包含n条记录的数据集 算法输出 降维或压缩后的数据集 算法思想 ?1.计算所有样本的均值m和协方差矩阵S: ?2.计算S的特征值,并由大到小排序: ?3.选择前n'个特征值对应的特征矢量作成一个变换矩阵E=[e1,e2, -,

《算法》第四版 IDEA 运行环境的搭建

<算法>第四版 IDEA 运行环境的搭建 新建 模板 小书匠 在搭建之初,我是想不到会出现如此之多的问题.我看了网上的大部分教程,都是基于Eclipse搭建的,还没有使用IDEA搭建的教程.我相信许多读者跟我一样,在学习Java的时候没有使用过命令行编译的形式去运行Java代码,直接使用Eclipse工具去进行开发的,因此,当看到书中 % java BinarySerach xxx.txt < xxx.txt 的时候,不免有点不知所措.笔者现在使用的IDE是IDEA,因此是想要在IDEA