回溯法概述

回溯法概述

——通用的解题方法、优化的穷举法。


一、

针对问题:需要找出它的解集,或者要求回答什么解时满足条件的最佳解时。

基本做法:搜索,或者时一种组织的井井有条的,能避免不必要搜索的穷举式搜索法(剪枝)。

主要利用:深度游先搜索策略

(1)递归回溯

(2)迭代回溯

(3)子集树算法框架

(4)排列树算法框架


二、直观印象

(1)“通用的解题方法”,可以搜索所有解或最优解,系统的又带跳跃性的搜索算法。

(2)深度优先搜索解空间树。先判断结点是否包含问题的解。不包含跳过这个子树,包含进入子树深度优先搜索。

(3)求所有解、最优解:回溯到根,根节点的所有子树已经被搜索。求任意解:只要搜到一个解就可以。

任务描述:

(1)明确定义问题的解空间,每个解可以用解向量表示。

(2)并非解空间所有元素都是问题的解。

  • 存在性问题:(可行解)求满足条件的一个或全部元组,不存在返回No
  • 优化问题:(最优解)求满足条件,使目标函数最大的元组。

剪枝函数——减少问题求解所需时即生成的状态结点数。

①约束函数:提高效率,避免无谓搜索。

②限界函数:(最优化问题)剪去不可能包含最优结点的子树。

问题的解空间:

  • 解向量:n元形式(x1,x2,x3……xn)
  • 显约束:对xi取值限定(背包问题每件物品xi=0/1)
  • 隐约束:为满足解,对不同分量施加的约束(wi小于背包容量)
  • 解空间:满足显式约束的所有向量。

 


三、生成问题的基本方法:

1.

  • 三个概念:
  • 扩展结点:一个正在产生儿子的结点
  • 活结点:一个自身已经生成,儿子还没有全部生成
  • 死结点:一个所有儿子已经产生的结点。

2. 

1.深度优先的问题状态生成法:

①扩展节点R,一旦产生一个儿子C,C就是新的扩展节点。R变为活结点。

②穷尽对子树C的搜索后,C变为死结点,R变为扩展节点。R继续生成下一个儿子。

2.宽度优先的问题状态生成法:

在一个扩展结点变为死结点之前,一直是扩展节点。

3.回溯:

限界函数处死一些不满足条件的活结点。

3. 解空间树:

1.子集树:解不定长

2.排列树:解定长

 


 四、基本思想和步骤、代码框架:

有限离散问题总可以用穷举法求得问题的全部解。

求得了满足约束条件D的部分解(x1,x2,……xi) ,

①若存在xi+1使得(x1,x2,……xi,xi+1)满足D,继续添加xi+2;

②所有的xi+1均不满足,去掉xi,回到(x1,x2,……xi-1)继续进行。

解题步骤:

①针对所给问题,定义问题的解空间;

②确定易于搜索的解空间结构;

③以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。

 复杂性:

h(n)是根节点到叶子结点的最长距离。

只要一个解:  O(h(n)) 

所有解:O(2^h(n))或O(h(n)!)

代码框架:

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))//约束条件&&限界条件
                 backrtrack(t+1);    
        }
}

 2.迭代回溯:

/*f(n,t) g(n,t)表示在当前扩展结点处,未搜索过的结点的起始编号和终止编号*/
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--;//t变成了死结点,回溯
    }
}

3.子集树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);
        }
}

4.排列树O(n!)

void backtrack(int t){
    if(t>n) output(x);
    else
        for(int i=t;i<=n;i++){//每个结点i都可以放在t这个位置
            swap(x[t],x[i]);
            if(legal(t)) backtrack(t+1)
            swap(x[t],x[i]);
        }
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hnu哈哈

请接受直女的么么哒????

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值