(九)分支限界法

2023-05-16


       分支限界法(branch and bound method)按广度优先策略搜索问题的解空间树,在搜索过程中,对待处理的节点根据限界函数估算目标函数的可能取值,从中选取使目标函数取得极值(极大或极小)的结点优先进行广度优先搜索,从而不断调整搜索方向,尽快找到问题的解。分支限界法适合求解最优化问题。



 1、分支限界法思想

       上节中回溯法是从根节点出发,按照深度优先的策略搜索问题的解空间树,在搜索过程中,如果某点所代表的部分解不满足约束条件,则对该节点为根的子树进行剪枝;否则继续按照深度优先的策略搜索以该结点为根,当搜索到一个满足的约束条件的叶子结点时,就找到了一个可行解。


       分支限界法首先要确定一个合理的限界函数(bound funciton),并根据限界函数确定目标函数的界[down ,up],按照广度优先策略搜索问题的解空间树,在分直结点上依次扩展该结点的孩子结点,分别估算孩子结点的目标函数可能值,如果某孩子结点的目标函数可能超出目标函数的界,则将其丢弃;否则将其加入待处理结点表(简称PT表),依次从表PT中选取使目标函数取得极值的结点成为当前扩展结点,重复上述过程,直到得到最优解。




2、TSP问题中使用分支限界法

       【TSP问题】:

       TSP问题是指旅行家要旅行n个城市,要求各个城市经理且仅经理依次然后回到出发城市,并要求所走的路程最短。我们以下图的无限图为例,采用分支限界法解决这个问题。




       该无向图对应的代价矩阵如下所示:


       代价矩阵是1到1,1到2,1到3,1到4,1到5距离写在第一行,第二行为2到1,2到2,2到3,2到4,、、、依次


       (1)找到目标函数的界。上界为,采用贪心算法求得上界,从节点1开始到节点3--->5--->4--->2--->1,路径,即为图中红色圈的路径,其路径长度为C=1+2+3+7+3=16。

下界为矩阵中每行中两个最小的相加,所有的行加起来的和的一半。( (3+1)+(3+6)+(1+2)+(3+4)+(2 +3) )/2=14 

所以求得界为[14,16]。

       (2)计算每个节点的限界值。

计算目标函数(限界函数),lb分为三部分,第一部分是经过路径的长度相加的2倍,加上第二部分离着路径首尾节点最近的距离相加(不在已知路径上的),加上第三部分除了路径上节点,矩阵中两个最短的距离相加,最后这三部分和相加,得到的结果除以2便是每个节点的限界值。

       (3)画出PT图。如下所示。





 

              根据上述所述得到最优解1-->3-->5-->4-->2-->1



【C代码】:

//分支限界法
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#define INF 100000
using namespace std;
/*  n*n的一个矩阵  */
int n;
int mp[22][22];//最少3个点,最多15个点
/*输入距离矩阵*/
void in()
{
    scanf("%d",&n);
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i==j)
            {
                mp[i][j]=INF;
                continue;
            }
            scanf("%d",&mp[i][j]);
        }
    }
}
struct node
{
    int visp[22];//标记哪些点走了
    int st;//起点
    int st_p;//起点的邻接点
    int ed;//终点
    int ed_p;//终点的邻接点
    int k;//走过的点数
    int sumv;//经过路径的距离
    int lb;//目标函数的值
    bool operator <(const node &p )const
    {
        return lb>p.lb;
    }
};
priority_queue<node> q;
int low,up;
int inq[22];
//确定上界
int dfs(int u,int k,int l)
{
    if(k==n) return l+mp[u][1];
    int minlen=INF , p;
    for(int i=1; i<=n; i++)
    {
        if(inq[i]==0&&minlen>mp[u][i])/*取与所有点的连边中最小的边*/
        {
            minlen=mp[u][i];
            p=i;
        }
    }
    inq[p]=1;
    return dfs(p,k+1,l+minlen);
}
int get_lb(node p)
{
    int ret=p.sumv*2;//路径上的点的距离
    int min1=INF,min2=INF;//起点和终点连出来的边
    for(int i=1; i<=n; i++)
    {
        if(p.visp[i]==0&&min1>mp[i][p.st])
        {
            min1=mp[i][p.st];
        }
    }
    ret+=min1;
    for(int i=1; i<=n; i++)
    {
        if(p.visp[i]==0&&min2>mp[p.ed][i])
        {
            min2=mp[p.ed][i];
        }
    }
    ret+=min2;
    for(int i=1; i<=n; i++)
    {
        if(p.visp[i]==0)
        {
            min1=min2=INF;
            for(int j=1; j<=n; j++)
            {
                if(min1>mp[i][j])
                min1=mp[i][j];
            }
            for(int j=1; j<=n; j++)
            {
                if(min2>mp[j][i])
                min2=mp[j][i];
            }
            ret+=min1+min2;
        }
    }
    return ret%2==0?(ret/2):(ret/2+1);
}
void get_up()
{
    inq[1]=1;
    up=dfs(1,1,0);
}
void get_low()
{
    low=0;
    for(int i=1; i<=n; i++)
    {
        /*通过排序求两个最小值*/
        int min1=INF,min2=INF;
        int tmpA[22];
        for(int j=1; j<=n; j++)
        {
            tmpA[j]=mp[i][j];
        }
        sort(tmpA+1,tmpA+1+n);//对临时的数组进行排序
        low+=tmpA[1];
    }
}
int solve()
{
    /*贪心法确定上界*/
    get_up();
    
    /*取每行最小的边之和作为下界*/
    get_low();
    
    /*设置初始点,默认从1开始 */
    node star;
    star.st=1;
    star.ed=1;
    star.k=1;
    for(int i=1; i<=n; i++) star.visp[i]=0;
    star.visp[1]=1;
    star.sumv=0;
    star.lb=low;
    
    /*ret为问题的解*/
    int ret=INF;
    
    q.push(star);
    while(!q.empty())
    {
        node tmp=q.top();
        q.pop();
        if(tmp.k==n-1)
        {
            /*找最后一个没有走的点*/
            int p;
            for(int i=1; i<=n; i++)
            {
                if(tmp.visp[i]==0)
                {
                    p=i;
                    break;
                }
            }
            int ans=tmp.sumv+mp[p][tmp.st]+mp[tmp.ed][p];
            node judge = q.top();
            
            /*如果当前的路径和比所有的目标函数值都小则跳出*/
            if(ans <= judge.lb)
            {
                ret=min(ans,ret);
                break;
            }
            /*否则继续求其他可能的路径和,并更新上界*/
            else
            {
                up = min(up,ans);
                ret=min(ret,ans);
                continue;
            }
        }
        /*当前点可以向下扩展的点入优先级队列*/
        node next;
        for(int i=1; i<=n; i++)
        {
            if(tmp.visp[i]==0)
            {
                next.st=tmp.st;

                /*更新路径和*/
                next.sumv=tmp.sumv+mp[tmp.ed][i];

                /*更新最后一个点*/
                next.ed=i;

                /*更新顶点数*/
                next.k=tmp.k+1;

                /*更新经过的顶点*/
                for(int j=1; j<=n; j++) next.visp[j]=tmp.visp[j];
                next.visp[i]=1;

                /*求目标函数*/
                next.lb=get_lb(next);
                
                /*如果大于上界就不加入队列*/
                if(next.lb>up) continue;
                q.push(next);
            }
        }
    }
    return ret;
}
int main()
{
    in();
    printf("%d\n",solve());
    return 0;
}



3、分支限界法解决0/1背包问题。

       在这里只写个思路,相对来说也是比较简单的。

       (1)首先将背包按照价值由大到小进行排列。

       (2)找到上界和下界,背包问题的下界把第一个价值最大的装入背包。上界,采用背包问题的贪心算法(三种策略)最终求得上界。

       (3)限界函数ub=v+(W-w)*(v i+1    /    w i+1)

       (4)画PT表格,每个节点进行判断是否剪枝。最终得到最优解。



算法设计与分析大概总结到这。


       学习是一点一点深入的,这一点体会是多么的深刻,就像我的牙齿一样,黑的部分不是一天两天能黑的,牙疼的引起也不是一天两天的事情,要有牙齿病菌的这种精神!~~~





本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

(九)分支限界法 的相关文章

随机推荐

  • 操作系统笔记(含王道计算机考研——操作系统课件)

    操作系统 xff08 OS xff09 笔记根据B站王道计算机考研 操作系统视频整理所得 xff0c 视频链接 xff1a https b23 tv 0I2qex视频中所用课件 xff1a 链接 xff1a https pan baidu
  • ERROR 1118 (42000): Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ...

    在创建数据库表时报错 之前已经在数据库里创建了多张表 xff0c 但在创建其中一张数据库表时报如下错 xff1a ERROR span class token number 1118 span span class token punctu
  • Ubuntu Gnome下怎样修改应用的图标icon

    我在我机器上安装了一个matlab 但在软件搜索里找不到matlab 我发现是matlab没有对应的 desktop文件 顺便我将matlab的图标也修改下 步骤如下 1 准备一个icon图像文件 如我这里的文件名为matlab png 将
  • DOS,WINDOWS递归删除指定文件夹或文件

    DOS xff0c WINDOWS递归删除指定文件夹或文件 64 REM 64 REM Name 递归删除指定的目录 xff0c 请把此文件放在你希望执行的那个目录 64 REM Desciption 64 REM Author amosr
  • macOS 使用 - 使用系统屏幕共享(VNC)

    文章目录 关于 屏幕共享 应用 权限开启 开始连接 使用 Apple ID 连接 使用 IP 连接 连接成功 断开连接 参考 关于 屏幕共享 应用 macOS 自带屏幕共享功能 路径为 System Library CoreServices
  • 免费ddns f3322.net使用脚本更新公网ip小记

    话说今天服务器域名访问不了 xff0c 路由器也访问不了 xff0c 另听说停电了 xff0c 估计是ddns没有更新 下午到现场一看 xff0c c7v2的ddns显示未登陆 xff0c 因为这货刷了us固件 xff0c 能用的ddnd只
  • Unresolved reference: databinding 模块化,组件化报错

    不要只在 总的 libaray 中添加 dataBinding span class token punctuation span enabled span class token operator 61 span span class t
  • Android lottie java.lang.IllegalStateException: Missing values for keyframe

    使用Lottie动画的时候 xff0c 运行发现了此报错 xff0c 版本为2 4 0 xff0c 在经过几番的测试后 xff0c 更改了资源文件和xml里面的配置也不大行 tips 一定要在xml里面配置资源文件 xff0c 当你把资源文
  • 追求技术之路 - 那些陪伴我的书籍

    如今已经在广州一家嵌入式公司实习 xff0c 分享大学里度过的一些书籍 xff0c 有些还没读完 xff0c 个人比较喜欢经典书籍 xff0c 研读起来就有种奇妙的感觉 xff0c 比起人与人之间的复杂的关系 xff0c 书籍带给我的感觉很
  • 详解蓝牙标准中的GFSK调制

    简介 GFSK是一种简单但应用广泛的调制方式 xff0c 在蓝牙和802 11等无线通信标准中都有应用 802 11跳频FHSS时所用的调制方式是GFSK 2和GFSK 4 xff0c 采用BT 61 0 5的高斯滤波器 在GFSK 2和G
  • ajax入门 不要畏惧 很简单 进了门一切都好学多了

    以前总是听别人说ajax是多么的好 xff0c 然后自己就去借了本书看 xff0c 哇塞感觉好难哦 xff0c 什么介绍javascript html css xff0c 还有很多一些东西 看的那个难啊 xff0c 然后就是硬着头皮把它给看
  • IntelliJ IDEA With Git

    记录下Git如何与IntelliJ IDEA协作 文章目录 环境准备IntelliJ IDEA With Git 开发过程1 初次获取远端代码2 查看远端仓库分支3 将指定的远端分支同步到本地 xff08 建议同远端名一致 xff09 4
  • 环形缓冲区(ringbuffer)

    环形缓冲区 xff08 ringbuffer xff09 环形缓冲区是嵌入式系统中十分重要的一种数据结构 xff0c 比如在串口处理中 xff0c 串口中断接收数据直接往环形缓冲区丢数据 xff0c 而应用可以从环形缓冲区取数据进行处理 x
  • Gson解析异常:Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $

    首先检查你的retrofit配置是否正确 xff0c 解析异常 addConverterFactory GsonConverterFactory create 在这里修改成这个gson的 Retrofit retrofit 61 new R
  • leetcode|多线程专题

    1114 按序打印 我们提供了一个类 xff1a public class Foo public void one print 34 one 34 public void two print 34 two 34 public void th
  • OpenCV实战(1)——OpenCV与图像处理基础

    OpenCV实战 xff08 1 xff09 OpenCV与图像处理基础 0 前言1 OpenCV 基础1 1 安装 OpenCV1 2 OpenCV 主要模块1 3 使用 Qt 进行 OpenCV 开发 2 OpenCV 图像处理基础2
  • 1.机器视觉标准框架学习

    在工业机器视觉上 xff0c 常见的图像处理库有opencv halcon visionpro sherlcok等 其中visionpro和sherlcok是拖拽式编程 xff0c 方便用户开发视觉项目 但对于opencv 和halcon则
  • Gitlab权限说明

    Gitlab权限管理 Gitlab用户在组中有五种权限 xff1a Guest Reporter Developer Master Owner Guest xff1a 可以创建issue 发表评论 xff0c 不能读写版本库 Reporte
  • 二进制的浪漫

    0 基本性质 0 1 交换律 相同运算符下可任意交换 xff0c 不同的运算符不可交换 0 2 结合律 相同运算符是可结合的 0 3 分配律 a amp b
  • (九)分支限界法

    分支限界法 xff08 branch and bound method xff09 按广度优先策略搜索问题的解空间树 xff0c 在搜索过程中 xff0c 对待处理的节点根据限界函数估算目标函数的可能取值 xff0c 从中选取使目标函数取得