算法设计与分析复习--分支界限法

news2024/7/4 4:52:41

文章目录

  • 上一篇
  • 分支界限法性质
  • 装载问题
  • 0-1背包问题
  • 单源最短路问题
  • 最大团问题
  • 下一篇

上一篇

算法设计与分析复习–回溯法(二)

分支界限法性质

分支界限法是按广度优先策略或最小耗费优先遍历问题的解空间树。

搜索解空间:

  1. 子集树
  2. 排列树

搜索方式:广度优先遍历(队列)或最小耗费优先(堆)

方法:确定解空间,设计合适的限界函数(在拓展时删除不必要的孩子结点),组织活结点表

但是由于每一层对应的cw, rw是不同的,所以需要用一个node的数据结构存储每一个节点的

装载问题

问题描述:n个集装箱要装到2艘重量分别 c 1 c_1 c1, c 2 c_2 c2的货轮,其中集装箱 i i i的重量为 w i w_i wi器满足集装箱重量之和小于两轮船载重。

最优装载方案:将第一艘船尽可能装满,将剩余的货箱装到第二搜轮船上。

约束函数:所装货物重量小于第一艘船载重
上界函数是:已装重量+剩余重量上界

使用队列的方式

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 110;

int a[N], n, c1, sum = 0, bw = 0;

struct node
{
    int idx; // 层数
    int cw;  // 当前层的重量
    int rw;  // 剩余的重量
};

void bfs()
{
    queue<node> q;
    q.push({0, 0, sum});

    while (q.size())
    {
        auto t = q.front();
        q.pop();

        bw = max(bw, t.cw); // 更新最大重量

        // 左扩展
        if (t.idx < n && t.cw + a[t.idx] <= c1)
        {
            q.push({t.idx + 1, t.cw + a[t.idx], t.rw - a[t.idx]});
        }
        // 右扩展
        if (t.idx < n && t.cw + t.rw > bw)
        {
            q.push({t.idx + 1, t.cw, t.rw - a[t.idx]});
        }
    }
}

int main()
{
    scanf("%d%d", &n, &c1);

    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
        sum += a[i];
    }

    bfs();
    printf("%d\n", bw);
    return 0;
}

在这里插入图片描述

利用优先级进行查找时
我们将利用当前结点的价值上界
c w + r w cw + rw cw+rw
进行堆的构造
重构堆需要

priority_queue<node, vector<node>, cmp> heap;
cmp为比较函数,不过要比较符相反

在这里插入图片描述
例如greater是返回更大的
而构造小根堆就用greater

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 110;

int a[N], n, c1, sum = 0, bw = 0;

struct node
{
    int idx; // 层数
    int cw;  // 当前层的重量
    int rw;  // 剩余的重量
};

struct cmp
{
    bool operator ()(const node &x, const node &y) const
    {
        return (x.cw + x.cw) < (y.cw + y.rw); // 优先队列的优先级按当前上界要用更大排,这里就要是小于
    }
};


void bfs()
{
    priority_queue<node, vector<node>, cmp > heap;
    heap.push({0, 0, sum});

    while (!heap.empty())
    {
        auto t = heap.top();
        heap.pop();

        bw = max(bw, t.cw); // 更新最大重量

        // 左扩展
        if (t.idx < n && t.cw + a[t.idx] <= c1)
        {
            heap.push({t.idx + 1, t.cw + a[t.idx], t.rw - a[t.idx]});
        }
        // 右扩展
        if (t.idx < n && t.cw + t.rw > bw)
        {
            heap.push({t.idx + 1, t.cw, t.rw - a[t.idx]});
        }
    }
}

int main()
{
    scanf("%d%d", &n, &c1);

    for (int i = 0; i < n; i++)
    {
        scanf("%d", &a[i]);
        sum += a[i];
    }

    bfs();
    printf("%d\n", bw);
    return 0;
}

在这里插入图片描述


由于优先队列的方式更难一些所以后面实现都是优先队列的方式

0-1背包问题

求法与装载问题一样,不如说装载问题特化成了0-1背包问题

但是在右剪枝的求法上和回溯法一样
但是bound函数用法不同了,bound就是求上界的函数,并且求得是当前结点的上界

左剪枝:不超过背包容量
右剪枝:cv + rv >= bv
rv是利用贪心背包的方式求得的

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>

using namespace std;
typedef pair<double, double> PDD;

const int N = 110;

int n, c;
vector<PDD> ob;
double bv = 0, sv = 0; // 将bv, sv初始化为0

struct node
{
    int idx;
    double cw;
    double cv;
    double ub;
    bool operator< (const node &x) const
    {
        return ub < x.ub;//利用ub堆排序
    }
};

bool cmp(PDD x, PDD y)
{
    return (x.second / x.first) > (y.second / y.first);//贪心排序
}

double bound(node x)
{
    double rcv = x.cv, rw = c - x.cw;
    int i = x.idx;//不同于回溯法,在输入时改变i的值,因为要计算当前结点的上界
    while (i < n && ob[i].first <= rw)
    {
        rw -= ob[i].first;
        rcv += ob[i].second;
        i++;
    }
    if (i < n)
        rcv += rw * (ob[i].second / ob[i].first);
    return rcv;
}

void bfs()
{
    priority_queue<node> heap;
    heap.push({0, 0, 0, bound({0, 0, 0, 0})}); // 初始节点的上界需要计算

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        printf("{%d, %d, %.1lf}\n", (int)t.cw, (int)t.cv, t.ub);//搜索顺序可视化

        if (t.idx == n)//到达叶子结点
        {
            if (t.cv > bv)
            {
                bv = t.cv;
            }
            continue; 
        }

        if (t.cw + ob[t.idx].first <= c) // 向左走
            heap.push({t.idx + 1, t.cw + ob[t.idx].first, t.cv + ob[t.idx].second, t.ub}); 
            
        node tmp = {t.idx + 1, t.cw, t.cv, bound({t.idx + 1, t.cw, t.cv, 0})};//需要填两次,定义临时变量
        if (bound(tmp) > bv)
            heap.push(tmp); // 向右走
    }
}

int main()
{
    scanf("%d%d", &n, &c);

    for (int i = 0; i < n; i++)
    {
        double w, v;
        scanf("%lf%lf", &w, &v);
        sv += v;
        ob.push_back({w, v});
    }

    sort(ob.begin(), ob.end(), cmp);

    bfs();
    printf("%d\n", (int)bv);

    return 0;
}

在这里插入图片描述
在这里插入图片描述

单源最短路问题

问题描述:给定一个带权有向图G = (V, E), 每条边的权值是一个正整数, 给定V中的一个顶点S,称作源点。要求:计算从源点到其他所有顶点的最短路径长度。

AcWing 850. Dijkstra求最短路 II

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef pair<int, int> PII;

const int N = 1e6 + 10;

int h[N], e[N], w[N], ne[N], idx = 0;
int dist[N], pre[N];
vector<int> ans;
bool st[N];
int n, m;

void add (int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}

void traceback(int k)
{
    if (k == 0) return;
    ans.push_back(k);
    traceback(pre[k]);
}

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});// first表示距离, second表示节点编号,这是因为在优先队列中是优先按元祖第一个元素进行排序

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;// ver表示节点编号

        if (st[ver])continue;
        st[ver] = true;

        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])// 因为要遍历Ver相连的所有边i所以提前将源点到ver的最短距离记作distance, 而w[i]记录的是第i个节点到j的距离(权重)i是与ver相连的边 
            // 将与ver相连的边更新为最短路径值,j是i的下一条边是一个指针关系
            {
                dist[j] = distance + w[i];
                pre[j] = ver;
                heap.push({dist[j], j});
            }

        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    else {
        traceback(n);
        reverse(ans.begin(), ans.end());
        puts("最短路径为:");
        for (auto i : ans)
            printf("%d ", i);
            puts("");
        return dist[n];
    }
}

int main ()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < m; i ++)
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add (a, b, c);
    }
    printf("路径长度为:%d", dijkstra());

    return 0;
}

在这里插入图片描述

最大团问题

问题描述:给定无向图G = (V, E)。如果 U ⊆ V U\subseteq V UV, 求对任意 u , v ∈ U u, v \in U u,vU ( u , v ) ∈ E (u, v) \in E (u,v)E, 则称U是G的完全子图。
最大团就是一个图含顶点数最大的完全图,且要是这个图的子集。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

const int N = 110;

int g[N][N], n, m, bn;
vector<int> ans;

struct node
{
    int idx;
    int cn;
    vector<int> x;
    int un;
    bool operator< (const node &p) const{
        return un < p.un;
    }
};

bool constrain(node c)
{
    for (int i = 0; i < c.idx - 1; i ++)//这里i不能到c.idx不然就会有它自身到自身为0会返回false,
    {
        if (c.x[i] == 1 && g[c.idx][i + 1] == 0)//x的下标是从0开始,而g[i][j]的下标是从1开始,所以要进行调整
            return false;
    }
    return true;
}

void bfs()
{
    priority_queue<node> heap;
    heap.push({0, 0, {}, n});
    
    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();
        
        if (t.idx == n){
            if (t.cn > bn){
                ans = t.x;
                bn = t.cn;
            }
            continue;
        }
        
        node tmp = {t.idx + 1, t.cn + 1, t.x, t.un};
        tmp.x.push_back(1);//要提前加入,否则判断是少条件
        
        if (constrain(tmp))
            heap.push(tmp);
        
        tmp = {t.idx + 1, t.cn, t.x, t.un - 1};
        tmp.x.push_back(0);
        if (tmp.un >= bn)
            heap.push(tmp);
        
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    
    for (int i = 0; i < m; i ++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        g[a][b] = g[b][a] = 1;
    }
    
    bfs();
    printf("%d\n", bn);
    for (auto val : ans) {
        printf("%d ", val);
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

下一篇

未完待续

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

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

相关文章

2024贵州大学计算机考研分析

24计算机考研|上岸指南 贵州大学 贵州大学计算机科学与技术学院&#xff08;贵州大学省级示范性软件学院&#xff09;位于贵州省贵阳市花溪区贵州大学东校区。 计算机科学与技术学院&#xff08;软件学院&#xff09;自1972年创办计算机软件本科专业开始&#xff0c;至今已有…

sso 四种授权模式

单点登录 单点登录&#xff0c;英文是 Single Sign On&#xff08;缩写为 SSO&#xff09;。即多个站点共用一台认证授权服务器&#xff0c;用户在站点登录后&#xff0c;可以免登录访问其他所有站点。而且&#xff0c;各站点间可以通过该登录状态直接交互。例如&#xff1a; …

aspera替代方案,镭速大文件传输解决方案替代

相信不少的互联网用户对于传输软件aspera并不陌生&#xff0c;但是市面上有没有哪一些aspera替代方案&#xff0c;aspera替代方案是否比aspera更加能够解决数据传输的需求&#xff0c;是一个值得思考的事情&#xff0c;那么我们先来了解一下aspera以及aspera替代方案。 了解Asp…

[Linux] shell条件语句和if语句

一、条件语句 1.1 测试 test 测试文件的表达式是否成立 格式&#xff1a;test 条件表达式 [ 条件表达式 ] 选项作用-d测试是否为目录-e测试目录或文件是否存在-a测试目录或文件是否存在-f测试是否为文件-r测试当前用户是否有权限读取-w测试当前用户是否有权限写入-x测试当前…

csdn最新最全pytest系列——pluggy插件源码解读(一)HookspecMarker类和HookimplMarker类分析

简介 pluggy是一个非常优秀的插件系统&#xff0c;它是理解pytest的核心&#xff0c;只有理解了pluggy的原理&#xff0c;才能更好的理解和使用pytest&#xff0c;否则见到了pytest的很多应用都会感觉很难理解 pluggy插件总共的代码量不足一千行&#xff0c;而实现的功能却是…

SpringBoot : ch06 整合 web (一)

前言 SpringBoot作为一款优秀的框架&#xff0c;不仅提供了快速开发的能力&#xff0c;同时也提供了丰富的文档和示例&#xff0c;让开发者更加容易上手。在本博客中&#xff0c;我们将介绍如何使用SpringBoot来整合Web应用程序的相关技术&#xff0c;并通过实例代码来演示如何…

Axios简单使用与配置安装-Vue

安装Axios npm i axios main.js 导入 import Axios from axios Vue.prototype.$axios Axios简单发送请求 get getTest() {this.$axios({method: GET,url: https://apis.jxcxin.cn/api/title?urlhttps://apis.jxcxin.cn/}).then(res > {//请求成功回调console.log(res)}…

使用ChatGPT创建Makefile构建系统:使用Make运行Docker

使用ChatGPT创建Makefile构建系统&#xff1a;使用Make运行Docker 芯语芯愿&#xff08;知乎/纷传/CSDN/&#xff09;&#xff1b;小石头的芯语芯愿&#xff08;微信公众号&#xff09; 开发高效现代的构建系统对于满足开发周期需求至关重要。原先&#xff0c;嵌入式开发者一…

多选按钮关联多个el-checkbox-group

需求&#xff1a; 如图设计稿&#xff0c;全部企业成员下面的数据来源与两个接口&#xff0c;点击全部企业成员需要勾选全部&#xff0c;下面选中全部企业成员要是选中状态&#xff0c;所以需要两个数组变量&#xff0c;两个el-checkbox-group来控制&#xff1b;有人可能会疑问…

Git远程库操作(GitHub)

GitHub 网址&#xff1a;https://github.com/ 创建远程仓库 远程仓库操作 命令名称作用git remote -v查看当前所有远程地址别名git remote add 别名 远程地址起别名git push 别名 分支推送本地分支上的内容到远程仓库git clone 远程地址将远程仓库的内容克隆到本地git pull 别…

OSG文字-HUD显示汉字示例(3)

显示文字是一种非常实用的技术&#xff0c;可以用来把一些重要的文字始终显示在屏幕上。HUD的全称是HeadsUpDisplay&#xff0c;即抬头显示&#xff0c;这种技术最早应用在军事战斗机上。 创建HUD显示的基本步骤如下: <1> 创建一个osg::Camera对象&#xff0c;设置视图、…

利用QRCode.js生成动态二维码页面

文章目录 QRCode.js简介HTML结构JavaScript生成动态二维码拓展功能1. 联系信息二维码2. Wi-Fi网络信息二维码 总结 &#x1f389;利用QRCode.js生成动态二维码页面 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博客主页&#xff1a;IT陈寒的博客&#x1f388;该系列文章专栏…

接口自动化测试实战经验分享,测试用例也能自动生成

作为测试&#xff0c;你可能会对以下场景感到似曾相识&#xff1a;开发改好的 BUG 反复横跳&#xff1b;版本兼容逻辑多&#xff0c;修复一个 BUG 触发了更多 BUG&#xff1b;上线时系统监控毫无异常&#xff0c;过段时间用户投诉某个页面无数据&#xff1b;改动祖传代码时如履…

OSG文字-osgText3D(5)

osgText3D 三维立体文字比二维平面文字显示效果更好&#xff0c;相对二维平面文字&#xff0c;它有非常好的立体显示效果。 在实际虚拟现实项目中&#xff0c;过多使用三维立体文字会降低染效率&#xff0c;加重渲染负担&#xff0c;相对平面二维文字&#xff0c;它占用的内存是…

for,while,until语句

一、for循环 读取不同的变量值&#xff0c;用来逐个执行同一组命令&#xff0c;经常使用在已经知道要进行多少次循环的场景。 1、基本格式 for 变量名称(注意是名称&#xff0c;不是变量$等) [ in 名称范围 ] (可以不写)do 执行内容 若满足循环则做什么动作do…

软考中级哪个科目最简单?

那必须是系统集成项目管理工程师&#xff01; 系统集成项目管理工程师考试内容少&#xff0c;题型简&#xff0c;报考门槛低&#xff0c;零基础就能报考&#xff0c;学习内容比较简单&#xff0c;接近工作和生活。 系统集成项目管理工程师证书是中国计算机技术职业资格&#…

【亚太杯思路助攻】2023年第十三届APMCM亚太地区大学生数学建模竞赛——(文末领取方式)

2023年第十三届APMCM亚太地区大学生数学建模竞赛——来啦&#xff01;&#xff01;&#xff01; 大家准备好了吗&#xff1f;别担心&#xff0c;【数模加油站】会像数模国赛、研赛一样&#xff0c;第一时间提供无偿解题思路、代码、参考文献等资料帮助大家。 祝各位小伙伴都能…

MySQL数据库常见错误及解决方案

“时记数据安全,共享优质资源”,数据库安全是指数据库数据的完整、真实、可靠和可用性。数据库也是一种软件系统,与其他软件系统一样也需要保护,需要采取一定的技术和一定的安全管理策略,保证数据库中的数据不被泄漏、不被破坏、不被修改或删除。本文列举MySQL数据库常见错…

QT搭建的Ros/librviz的GUI软件

1.前言 开发初期学习了下面博主的文章&#xff0c;也报了他在古月局的课&#xff0c;相当于感谢吧。 ROS Qt5 librviz人机交互界面开发一&#xff08;配置QT环境&#xff09;-CSDN博客​​​​​​​r 软件前期也是参考他的开源项目 GitHub - chengyangkj/Ros_Qt5_Gui_App …

Os-hackNos-3

Os-hackNos-3 一、主机发现和端口扫描 主机发现&#xff0c;靶机地址192.168.80.145 arp-scan -l端口扫描&#xff0c;开放了22和80端口 nmap -P -sV 192.168.80.145二、信息收集 访问80端口 find the Bug You need extra WebSec翻译 找到Bug 你需要额外的网络安全路径扫描 d…