05回溯法

news2024/12/25 1:41:31

文章目录

  • 装载问题
    • 回溯算法
    • 优化算法
    • 构造最优解
  • 0-1背包问题
  • 批处理作业调度问题
  • 图的M着色问题
  • N皇后问题
  • 最大团问题

回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。

回溯法思想
回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束;
而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。

回溯法一般步骤
(1)针对所给问题,确定问题的解空间: 首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则。
(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
在这里插入图片描述

  1. 问题的解空间

用回溯法解问题时,应明确定义问题的解空间,其至少应包含问题的一个(最优)解。

问题的解向量:回溯法希望一个问题的解能够表示成一个n元式(x1,x2,…,xn)的形式。

例如,n=3的0-1背包问题,其解空间由长度为3的0-1向量组成:
{(0,0,0),(0,1,0),(0,0,1),(1,0,0),
(0,1,1),(1,0,1),(1,1,0),(1,1,1)}

n=3的0-1背包问题的解空间可以用一颗完全二叉树来表示。

在这里插入图片描述

  1. 搜索过程

在这里插入图片描述
3. 递归回溯
回溯法对解空间作深度优先搜索,因此,在一般情况下用递归方法实现回溯法。

void backtrack (int t)  // t表示递归深度,即当前扩展结点在解空间树中的深度。
{     
 if (t>n) 
    output(x); //已到叶子结点,输出结果
 else  // f(n,t) ,g(n,t) :表示当前扩展结点处未搜索过的子树的起始编号和终止编号。
    for (int i=f(n,t);i<=g(n,t);i++) 
    { 
       x[t]=h(i);  //当前扩展节点处x[t]的第i个可选值。
       if (constraint(t)&&bound(t))       
            backtrack(t+1);
     } //constraint(t)、bound(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--;
    }
}
  1. 子集树与排列树

子集树:当所给问题是从n个元素的集合中找出满足某种性质的子集时,相应的解空间树。
如0-1背包问题。
在这里插入图片描述

遍历子集树需O(2^n)计算时间 
void backtrack (int t)
{  if (t>n) output(x);
    else
      for (int i=0;i<=1;i++) {
        x[t]=i;
        if (legal(t)) backtrack(t+1);
      }
  }

排列树:当所给的问题是确定n个元素满足某种性质的排列时,相应的解空间树。
如旅行售货员问题。
在这里插入图片描述

遍历排列树需要O(n!)计算时间 
void backtrack (int t)
{  if (t>n) output(x);
    else
      for (int i=t;i<=n;i++) {
        swap(x[t], x[i]);
        if (legal(t)) backtrack(t+1);
        swap(x[t], x[i]);
          }
       } 

装载问题

有n个集装箱要装上2艘载重量分别为C1和C2的轮船。其中集装箱i的重量为Wi,且(W1+W2+….+Wn<=C1+C2)。
装载问题是,是否有一个合理装载方案,可将这n个集装箱都装上这2个轮船,若有,请给出解决方案。

例如:
C1=C2=50, W=(10,40,40) 可以装载(10,40) (40)
C1=C2=55, W=(20,40,40) 无法装载

容易证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船。
将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近C1。由此可知,装载问题等价于特殊的0-1背包问题。
在这里插入图片描述

回溯算法

 void  backtrack( int t ) //搜索子集树中第t层子树
// n是集装箱数,cw是当前载重量,bestw是当前最优载重量,C是第一艘轮船载
   重量
{
if (t>n)  //到达叶结点
   { 
    if (cw>bestw)   
        bestw=cw;  //找到一个更好的方案
     return;   
     }
 if (cw+w[t]<=C)  //搜索左子树
   {    
    cw+=w[t]; x[t]=1; 
    backtrack( t +1);
    cw-=w[t]; //还原到上层结点    
    }
 x[t]=0; 
 backtrack( t +1); //搜索右子树           
 }

在这里插入图片描述

优化算法

void  backtrack( int t )
// 引入上界函数cw+r,剪去不含最优解的子树;r是剩余未判定的集装箱重量
{
if (t>n)  
  {  
  if (cw>bestw) 
     bestw=cw; //找到一个更好的方案
  return;   
  }
  r-=w[t];   //进入本层前,先计算剩余未判断的物品重量
 if (cw+w[t]<=C)  //搜索左子树
 {    
   cw+=w[t]; x[t]=1;
   backtrack( t +1);
   cw-=w[t];      
   } //还原到上层结点 
 if ( cw+r > bestw) //若余下的重量与已选择的重量之和可以超过前面已得的最优值                               
  {  
     x[t]=0; backtrack( t +1);  
  } //搜索右子树      
 r+=w[t];  //返回上层前,还要还原剩余载重量和。
 }

构造最优解

void  backtrack( int t )
{
if (t>n) 
{  
  if(cw >bestw)     
  {  bestw=cw;
     bestx[1:n] <- x[1:n];   
   } // bestx[]记录当前最优解
  return;          
 }
  r-=w[t];   
 if (cw+w[t]<=C)  //搜索左子树
  {    
   cw+=w[t];  x[t]=1;
   backtrack( t +1);
   cw-=w[t];     
    }
 if ( cw+r > bestw) 
  {  
     x[t]=0;
     backtrack( t +1); //搜索右子树  
   }
 r+=w[t];  //返回上层前,还要还原剩余载重量和。
 }

0-1背包问题

0-1背包问题是子集选取问题,也是NP难问题,其解空间可用子集树表示。

初始:cp=0;//当前价值
cw=0;//当前重量

设r是当前剩余物品价值总和,bestp是当前最优价值。
当cp+r≤bestp时,可剪去右子树。
计算右子树中解的上界更好的方法是将剩余物品依其单位重量价值排序后依次装入,直至装不下时,再装入该物品的一部分而装满背包,此时得到的价值是右子树中解的上界。
例如:n=4,c=7,p={9,10,7,4},w={3,5,2,1},单位重量价值分别为{3,2,3.5,4}。
按上述方法得到x=[1,0.2,1,1],相应价值22,即所求上界。
此时,判断右子树要剪掉的条件由cp+30≤bestp变为cp+22≤bestp,更容易被满足。
template<class Typew, class Typep>
Typep Knap<Typew, Typep>::Bound(int i)
{ // 计算上界,cp当前价值,cw当前重量
   Typew cleft = c - cw;  // 剩余容量
   Typep b = cp;
   // 以物品单位重量价值递减序装入物品
   while (i <= n && w[i] <= cleft) {
      cleft -= w[i];
      b += p[i];
      i++;
      }
   // 装满背包
   if (i <= n) b += p[i]/w[i] * cleft;
   return b;
}

批处理作业调度问题

问题描述:
给定 n 个作业的集合 j = {j1, j2, …, jn}。每一个作业 j[i] 都必须先由机器1处理,然后由机器2处理。作业 j[i] 需要机器 j 的处理时间为 t[j][i] ,其中i = 1, 2, …, n, j = 1, 2。对于一个确定的作业调度,设F[j][i]是作业 i 在机器 j 上的完成处理的时间。所有作业在机器2上完成处理的时间之和 称为该作业调度的完成时间之和。

在这里插入图片描述

批处理作业调度是要从 n 个作业的所有排列中找出有最小完成时间和的作业调度,所以批处理调度问题的解空间是一棵排列树。按照回溯法搜索排列树的算法框架,设开始时x = [1, …, n]是所给的 n 个作业,则相应的排列树由所有排列构成。

void Flowshop::Backtrack(int i)
//x当前作业调度,bestx当前最优作业调度; 
   f完成时间和,bestf当前最优值。
{   if (i > n) {
                   for (int j = 1; j <= n; j++)
                   bestx[j] = x[j];
                   bestf = f;  }
     else  for (int j = i; j <= n; j++)
         {  f1+=M[x[j]][1]; //M[][]作业处理时间
f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+M[x[j]][2];
            f+=f2[i];
            if (f < bestf) { Swap(x[i], x[j]);
                                 Backtrack(i+1);
                                 Swap(x[i], x[j]); }
            f1- =M[x[j]][1];
            f- =f2[i];
         }
}

图的M着色问题

给定无向连通图G和m种不同的颜色。用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中每条边的2个顶点着不同颜色。这个问题是图的m可着色判定问题。
若一个图最少需要m种颜色才能使图中每条边连接的2个顶点着不同颜色,则称这个数m为该图的色数。求一个图的色数m的问题称为图的m可着色优化问题。

解向量:(x1, x2, … , xn)表示顶点i所着颜色x[i]
可行性约束函数:顶点i与已着色的相邻顶点颜色不重复。

int  m;            //给定的颜色数
 int a[n][n];     //n个顶点图的邻接矩阵
 int x[n];         //存放着色方案
 int flag=0;     //是否M可着色
 解向量:(x1, x2,, xn)表示顶点i所着颜色x[i] 
 可行性约束函数:顶点i与已着色的相邻顶点颜色不重复。

bool Ok(int k)
{  // 着色方案是否可行
   for (int j=1;j<=n;j++)
   if ((a[k][j]==1)&&(x[k]==x[j])) return false;
   return true;
}

void  backtrack(t)    //判断图是否M可着色
       {   if (t>n)   //有着色方案
                {   flag=1;    return;   }    
           for (i=1; i<=m; i++)   //对M种颜色进行遍历
               {   x[t]=i;
                   if ( ok(t))  backtrack(t+1) ;   }
         return;
       }

在这里插入图片描述

N皇后问题

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

在这里插入图片描述

要素一: 解空间
一般想法:利用二维数组,用[i,j]确定一个皇后位置!
优化:
利用约束条件,只需一维数组即可!
x:array[1…n] of integer;
x[i]:i表示第i行皇后
x[i]表示第i行上皇后放第几列

要素二:约束条件
不同行:数组x的下标保证不重复
不同列:x[i]<>x[j] (i<=I,j<=n;i<>j)
不同对角线:abs(x[i]-x[j])<>abs(i-j)

要素三:状态树
将搜索过程中的每个状态用树的形式表示出来!

算法描述:
1.产生一种新放法
2.冲突,继续找,直到找到不冲突----不超范围
3.if 不冲突 then k<n ->k+1
k=n ->一组解
4.if 冲突 then 回溯

解向量:(x1, x2,, xn)
显约束:xi=1,2,,n
隐约束:1)不同列:xi≠xj
      2)不处于同一正、反对角线:|i-j≠|xi-xj|

bool Queen::Place(int k)
{
  for (int j=1;j<k-1;j++)  //填到第K行时,就与前1~(K-1)行都进行比较
  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++;
    else
      for (int i=1;i<=n;i++) {  //每层均有n种放法
        x[t]=i;  //放置本层皇后
        if (Place(t)) Backtrack(t+1);
      }
 }

最大团问题

给定无向图G=(V,E)。如果UV,且对任意u,vU有(u,v)E,则称U是G的完全子图。G的完全子图U是G的团当且仅当U不包含在G的更大的完全子图中。G的最大团是指G中所含顶点数最多的团。

解题思路:
首先设最大团为一个空团,往其中加入一个顶点,然后依次考虑每个顶点,查看该顶点加入团之后仍然构成一个团。
如果可以,考虑将该顶点加入团或者舍弃两种情况;如果不行, 直接舍弃,然后递归判断下一顶点。
对于无连接或者直接舍弃两种情况,在递归前,可采用剪枝策略来避免无效搜索。

解空间:子集树
可行性约束函数:顶点i到已选入的顶点集中每一个顶点都有边相连。
上界函数:有足够多的可选择顶点使得算法有可能在右子树中找到更大的团。

当前团的顶点数cn,最大团顶点数bestn,从根结点R开始,按照1、2、3、4、5的顺序深度搜索。

void Clique::Backtrack(int i) { // 计算最大团
  if (i > n) {   // 到达叶结点
      for (int j = 1; j <= n; j++) bestx[j] = x[j];
      bestn = cn;   return;}  //当前团的顶点数cn
   int OK = 1;
   for (int j = 1; j < i; j++)  // 检查顶点 i 与当前团的连接
   if (x[j] && a[i][j] == 0) { // i与j不相连
      OK = 0;  break; }
   if (OK) { // 进入左子树
      x[i] = 1;  cn++;
      Backtrack(i+1);
      x[i] = 0; cn--;}
   if (cn + n - i > bestn) { // 进入右子树
      x[i] = 0;
      Backtrack(i+1);}
}

在这里插入图片描述

回溯算法的效率在很大程度上依赖于以下因素:
(1)产生x[k]的时间;
(2)满足显约束的x[k]值的个数;
(3)计算约束函数constraint的时间;
(4)计算上界函数bound的时间;
(5)满足约束函数和上界函数约束的所有x[k]的个数。
好的约束函数能显著地减少所生成的结点数。但这样的约束函数往往计算量较大。因此,在选择约束函数时通常存在生成结点数与约束函数计算量之间的折衷。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175275.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

12. 字典dict类型详解

1. 基础知识 (1) 字典(dictionary)是Python中另一个非常有用的内置数据类型。 (2) 列表是有序的对象集合&#xff0c;字典是无序的对象集合。两者之间的区别在于&#xff1a;字典当中的元素是通过键来存取的&#xff0c;而不是通过偏移存取。 (3) 字典是一种映射类型&#xff…

Flowable进阶学习(三)流程、流程实例挂起与激活;启动、处理、结束流程的原理以及相关表结构与变动

文章目录流程挂起与激活流程实例挂起与激活启动、处理、结束流程的原理一、启动流程的原理启动一个流程实例时涉及到的表及表结构:ACT_RU_EXECUTION 运行时流程执行实例ACT_RU_IDENTITYLINK 运行时用户关系信息ACT_RU_TASK 运行时任务表ACT_RU_VARIABLE 运行时变量表二、处理流…

过滤器Filter总结

过滤器Filter1. 简介2. 快速入门3. 执行流程4. 使用细节4.1 拦截路径4.2 过滤器链5. 案例5.1 需求5.2 LoginFilter1. 简介 过滤器是JavaWeb三大组件之一&#xff08;Servlet、Filter&#xff0c;Listner&#xff09;&#xff1b; 作用&#xff1a; 把对资源&#xff08;servl…

Ubuntu22.04 安装 ssh

文章目录Ubuntu22.04 安装 ssh一、 环境配置二、 启动远程连接三、 开放端口四、 远程连接Ubuntu22.04 安装 ssh 一、 环境配置 安装 Ubuntu 系统后&#xff0c;我们首先需要配置管理员 root 用户&#xff1a; sudo passwd root然后&#xff0c;进行软件源的更换&#xff1a…

14 Java集合(Map集合+HashMap+泛型使用+集合面试题)

集合14.11 Map集合14.11.1 Map集合特点14.11.2 Map集合体系结构14.12 HashMap14.12.1 HashMap基本使用14.12.2 HashMap实际应用14.12.3 HashMap练习14.12.4 HashMap底层实现原理14.12.5 put的过程原码14.12.6 resize过程原码14.12.7 get的过程原码14.13 HashTable14.14 泛型高级…

5-1中央处理器-CPU的功能和基本结构

文章目录一.CPU的功能二.CPU的基本结构&#xff08;一&#xff09;运算器1.运算器的基本组成2.专用数据通路方式3.CPU内部单总线方式&#xff08;二&#xff09;控制器1.基本组成2.实现过程&#xff08;三&#xff09;寄存器一.CPU的功能 中央处理器&#xff08;CPU&#xff0…

并查集的入门与应用

目录 一、前言 二、并查集概念 1、并查集的初始化 2、并查集的合并 3、并查集的查找 4、初始化、查找、合并代码 5、复杂度 二、路径压缩 三、例题 1、蓝桥幼儿园&#xff08;lanqiaoOJ题号1135&#xff09; 2、合根植物&#xff08;2017年决赛&#xff0c;lanqiaoO…

SQL注入篇 - 布尔盲注及延时注入

数据来源 盲注 什么是盲注&#xff1a; 布尔盲注原理 布尔盲注流程 手工盲注思路&#xff08;以下的文章参考&#xff1a;DVWA-sql注入&#xff08;盲注&#xff09; - N0r4h - 博客园&#xff09; 手工盲注的过程&#xff0c;就像你与一个机器人聊天&#xff0c;这个机器人知…

DGSEA | GSEA做完了不要停,再继续比较一下有意义的通路吧!~

1写在前面 GSEA大家都会用了&#xff0c;但GSEA也有它自己的缺点&#xff0c;就是不能比较两个基因集或通路的富集情况。&#x1f912; 今天介绍一个Differential Gene Set Enrichment Analysis (DGSEA)&#xff0c;可以量化两个基因集的相对富集程度。&#x1f609; 2用到的包…

Java中的位运算及其常见的应用

文章目录1、位运算1.1 原码、反码、补码1.2 位运算符2、位运算的应用2.1 取模运算2.2 奇偶性判断2.3 交换变量的值2.4 加法运算1、位运算 1.1 原码、反码、补码 计算机中所有数据的存储和运算都是以二进制补码的形式进行的。a —> 97&#xff0c;A —> 65&#xff0c;‘…

深入学习Vue.js(十二)编译器

模板DSL的编译器 1.编译器概述 编译器实际上是一段程序&#xff0c;他用来将一种语言A翻译为另一种语言B。其中&#xff0c;A被称为源代码&#xff0c;B被称为目标代码。编译器将源代码翻译为目标代码的过程被称为编译。完整的编译过程通常包含词法分析、语法分析、语义分析、…

软件测试——测试用例

作者&#xff1a;~小明学编程 文章专栏&#xff1a;测试开发 格言&#xff1a;热爱编程的&#xff0c;终将被编程所厚爱。 目录 测试用例的设计方法 等价类 边界值 错误猜测法 判定表法&#xff08;使用于关系组合&#xff09; 设计步骤 具体例子 正交法 场景设计法…

Redis相关简介

1. Redis 简介 在这个部分&#xff0c;我们将学习以下3个部分的内容&#xff0c;分别是&#xff1a; ◆ Redis 简介&#xff08;NoSQL概念、Redis概念&#xff09; ◆ Redis 的下载与安装 ◆ Redis 的基本操作 1.1 NoSQL概念 1.1.1 问题现象 在讲解NoSQL的概念之前呢&am…

8. R语言画:散点图、直方图、条形图、箱线图、小提琴图、韦恩图

b站课程视频链接&#xff1a; https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新&#xff0c;但是要花钱&#xff0c;我花99&#x1f622;&#x1f622;元买了&#xff0c;感觉讲的没问题&#xff0c;就是知识点结构有点乱&#xff0c;有点废话&#xff09;&…

九大数据分析方法-综合型分析方法以及如何使用这九大分析方法

文章目录3 综合型分析方法3.1 相关性分析法3.1.1 直接相关3.1.2 间接相关3.2标签分析法3.3 MECE法4 如何使用九大方法本文来源&#xff0c;为接地气的陈老师的知识星球&#xff0c;以及付同学的观看笔记。3 综合型分析方法 3.1 相关性分析法 相关性分析法&#xff1a;寻找指标…

ROS2机器人编程简述humble-第二章-Executors .3.5

ROS2机器人编程简述humble-第二章-Parameters .3.4由于ROS2中的节点是C对象&#xff0c;因此一个进程可以有多个节点。事实上&#xff0c;在许多情况下&#xff0c;这样做是非常有益的&#xff0c;因为当通信处于同一进程中时&#xff0c;可以通过使用共享内存策略来加速通信。…

freeglut 在mfc 下的编译

freeglut 是OpenGL Utility Toolkit (GLUT) library 的替代版本&#xff0c;glut 应用广阔&#xff0c;但比较陈旧&#xff0c;很久没有更新。 我原来的opengl 用的是glut&#xff0c; 想更新到64位版本&#xff0c;怎么也找不到合适的下载。最后找到完全替代版本freeglut。fre…

【Linux】线程概念 | 互斥

千呼万唤始出来&#xff0c;终于到多线程方面的学习了&#xff01; 所用系统Centos7.6 本文的源码&#x1f449;【传送门】 最近主要是在我的hexo个人博客上更新&#xff0c;csdn的更新会滞后 文章目录1.线程的概念1.1 执行流1.2 线程创建时做了什么&#xff1f;1.3 内核源码中…

每刻和金蝶云星空接口打通对接实战

接通系统&#xff1a;每刻3000中大型企业在用&#xff0c;新一代业财税一体化解决方案提供商。旗下拥有每刻报销、每刻档案、每刻云票、每刻财务共享云平台等&#xff0c;助力企业实现财务数字化转型。对接系统&#xff1a;金蝶云星空金蝶K/3Cloud结合当今先进管理理论和数十万…

算法刷题打卡第72天:最少侧跳次数

最少侧跳次数 难度&#xff1a;中等 给你一个长度为 n 的 3 跑道道路 &#xff0c;它总共包含 n 1 个 点 &#xff0c;编号为 0 到 n 。一只青蛙从 0 号点第二条跑道 出发 &#xff0c;它想要跳到点 n 处。然而道路上可能有一些障碍。 给你一个长度为 n 1 的数组 obstacle…