算法导论:回溯法

基本思想

将所有的解按照一定结构排列,再进行搜索。一般解空间构造成树状结构,用深度优先搜索策略。

  1. 针对所给问题,定义问题的解空间。
  2. 确定易于搜索的解空间结构。
  3. 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

常用的剪枝函数:

  • 用约束函数在扩展结点处剪去不满足约束的子树。
  • 用限界函数剪去得不到最优解的子树。

如果解空间树中从根结点到叶结点的最长路径的长度为 h(n),则回溯法所需的计算空间通常为 O(h(n)),显式地存储整个解空间则需要 O(2h(n))O(h(n)!) 内存。

N 皇后问题

问题描述

n×n 格的棋盘上放置彼此不受攻击的 n 个皇后。任何 2 个皇后不放在同一行或同一列或同一斜线上。

  • n=1:显然成立
  • n=2,3:问题无解
  • n>4:可能存在多个解

四皇后解空间

向量 x=(x1,x2,x3,x4) 表示皇后的布局。分量 xi 表示第 i 行皇后的列位置。

xi 的取值范围 Si={1,2,3,4},故有 44 个可能解,(2,4,1,3) 是一个可行解,如下:

四皇后可行解

完全四叉树的所有叶子节点构成一个解空间,每个叶子节点就是一个可能解,满足要求的叶子节点就是可行解。

四皇后解空间

生成问题状态

  • 扩展结点:一个正在产生儿子的结点
  • 活结点:一个自身已生成但其儿子还没有全部生成的节点
  • 死结点:一个所有儿子都已经产生的结点
  • 深度优先的问题状态生成法:如果对一个扩展结点 R,一旦产生了它的一个儿子 C,就把 C 当做新的扩展结点。在完成对子树 C 的穷尽搜索之后,将 R 重新变成扩展结点,继续生成 R 的下一个儿子(如果存在)
  • 宽度优先的问题状态生成法:在一个扩展结点变成死结点之前,它一直是扩展结点,为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量

四皇后状态空间树

  • 四皇后状态空间树是 4 叉完全树
  • 约束方程:
    • 基本条件:1i,j4
    • 不在同一行:ij
    • 不在同一列:xixj
    • 不在同一个斜线:|xixj||ij|

四皇后状态空间树

伪代码

// 判断接下来的扩展节点是否符合要求
Boolean place(int k) {
	for(int i = 1; i < k; i++) {
  	if((abs(k-j) == abs(x[j]-x[k])) || (x[j] == x[k])) return false;
  }
  return true;
}

// 回溯法,n 表示n皇后
void backTrack(int t) {
	if(t > n) return;
  else {
  	for(int i = 1; i <= n; i++) {
    	x[t] = i;
      if(place(t)) backTrack(t+1);
    }
  }
}

回溯法

递归回溯

void backTrack(int t) {
  // 到达叶子节点,也就是一个可行解
	if(t > n) output(x);
  else {
  	for(int i = f(n,t); i <= g(n,t); i++) {
    	x[t] = h(i);
      if(constraint(t) && bound(t)) backTrack(t+1);
    }
  }
}

迭代回溯

void iterativeBacktrack() {
	int t = 1;
  while(t > 0) {
  	if(f(n,t) <= g(n,t)) {
    	for(int i = f(n,t); i <= g(n,t); i++) {
      	x[t] = h(i);
      	if(constraint(t) && bound(t)) {
        	if(solution(t)) output(x);
          else t++;
        }
      }
    }
    else t--;
  }
}
posted @   FireOnFire  阅读(136)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示