简单算法——回溯、贪心、动态规划

news2024/9/26 5:14:09

回溯法

回溯法=深度优先+剪枝,实质就是用递归代替for循环。
仍然是一种暴力遍历的手段,通常与递归配合使用,用于解决单纯for循环无法处理的问题,比如组合、切割、子集、排列等问题——比如求n个数里的长度为k的组合,需要k重循环,回溯使用递归代替一重循环,以此实现遍历。
使用代码对回溯法进行举例说明,也是回溯法的模板:

void backtracking(参数){
if(终止条件){叶子节点收集结果;
return ;}
for(集合元素集){
处理节点;
递归;
回溯操作;//执行到此步说明此次循环递归已经找到一个结果,
                 //需要回溯来找新的结果
return;}
}

组合

n个数找长度为k的所有组合,需要嵌套k个循环,回溯法通过递归控制循环层数,每层递归一个for循环,举例代码如下:

二维数组result用于保存结果
一维数组path用于记录组合
void backtracking(n,k,startindex){//n个数,k长度,当前起始位置index
if(path.size==k){//叶子节点,找到结果
result.push(path);
return;}
for(int i=startindex;i<=n;i++){
path.push(i);//装入一个元素
backtracking(n,k,i+1);//递归得到一个结果
path.pop();//弹出元素,进行回溯

N 皇后

nn的棋盘上放n个皇后,不能同行同列或处于对角线上,问有多少种摆法,将回溯法表示为树便于观察:
皇后回溯树
解释:将棋盘按行遍历,先在第一行的第i列放置皇后,再在放了第一个皇后的基础上在第二行放皇后,如此遍历。(示例是3
3的棋盘,该情况下皇后问题无解)。该树的深度为n。
先将该想法实现为代码如下:

设置三维数组result用于存储结果;
void backtarcking(chessboard,n,row){//n*n的棋盘,当前放置位置为第row行
if(row==n){//说明到了叶子节点,保存结果
result.pushback(chessboard);
return ;}
for(int i=0;i<n;i++){
if(isvalid(row,i,chessboard,n)//判断当前位置能否放置皇后,代码简单,略
{
chessboard[row][i]="Q";
backtrack(chessboard,n,row+1)//继续算法进行下一行的放置
chessboard[row][i]=" ";//一轮放置已完成,放置初始化进行下一轮
                                        //回溯,恢复原有,继续遍历

回溯法总结

代码其实都不难,判断结束条件并记录结果,for循环,内部处理节点,递归,回溯操作;说白了就是确定终止条件+单层递归逻辑两步,主要是用递归代替循环,尝试然后初始化(回溯)的思想。
涉及的难点和体现代码质量的地方就在于剪枝,在for循环内免去不必要的计算和判断可以大幅提高运行性能,这是需要多加考虑的点。

贪心算法

大题思路就是阶段性的局部最优=>整体最优,多数情况下无法严谨证明,题目一般也没啥规律和框架,多思考把握实践,运行结果正确就行,不用太钻牛角尖。

喂饼干问题

胃口和饼干两个数组,饼干大于胃口才能喂饱,问能喂饱多少小孩,策略是 用大饼干喂胃口大的孩子(局部最优)就能喂饱最多小孩(整体最优)。
直接代码展示:

//胃口g 1,2,7,10
//饼干s 1,3,5,9
sort(g);sort(s)//把饼干和胃口数组先从小到大排序,便于比较
int result=0;
int index=s.size()-1;
for(i=g.size()-1;i>=0;i--){
while(index>=0&&s[index]>=g[i]){//使用while便于控制条件,
 //饼干大于胃口成功投喂,判断index需大于0
result++;
index--;
}}
return result

饼干注意

循环不可颠倒,现在是由大饼干找小孩,吃一个换个饼干,如果由小孩找饼干,吃不上换个饼干,可能正好比上最大的,白循环了。
此外:用小饼干匹配小胃口也是可以。

划分字母区间

分割字符串区间使区间内的字符只出现在该区间内。例如:

s=ababcdacadefegde;//一个区间为ababcdaca,另一个为defegde,
                                      //第一个区间包含所有abc,第二个包含所有defg
                                    //输出[9,7]

思路为1,记录字符出现的最远位置,2,根据最远位置划分划分区间,首先先形成如下表格

0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
a  b  a  b  c  b  a  c  a  d  e   f    e   g   d   e 
8  5  8  5  7  5  8  7  8  14 15 11 15 13 14 15   最后出现位置
int hash[27]={0};
for(i=0;i<s.size();i++)//遍历形成最远位置数组
hash[s[i]-'a']=i;//s[i]-'a'=0,1,2 用于代表abcd,且i为最远出现位置,
//重复赋值即可得到字符的最远出现位置数组
vector result;
int left, right =0;
for(i=0;i<s.size();i++){
right=max(right,hash[s[i]-'a');//区间取到已包含字符的最远距离
//则其中出现的所有字符都被包含
if(i==right){
result.push_back(right-left+1);//返回区间长度
left=i+1;}}
return result;

贪心总结

思想很简单,局部到整体,但是有时候要借助创建的变量和方法确实难以想到,所以难点在于辅助的构造,而非算法本身。

动态规划

用于解决重叠子问题很多的问题,当前的每个状态都有上一个状态推导而来,可以说是大型的递归,我理解是在在运行中变化生成结果数组或者结果矩阵,故而称为动态规划。与贪心不同,贪心只是单纯地根据先有情况选出最优,而动态规划是需要推导得到最好结果。

一般流程

设置dp数组也就是结果集并搞懂dp[i]下标含义;
找到dp数组递推公式,即找到子问题的通解公式;
dp数组的初始化,具体问题具体分析;
确定遍历顺序;
输出打印dp数组,便于调试。

斐波那契数列

每个数为前两个数之和,求第n个数,以1,1开始。
直接递归或者单纯简单for循环

sum=dp[0]+dp[1];
dp[0]=dp[1];
dp[1]=sum;

就可以求,但是还是从简单问题理清思路,熟悉解决问题的一般流程。
dp[i]表示第i个数的值;
递推公式:dp[i]=dp[i-1]+dp[i-2]
数组初始化,dp[0]=1,dp[1]=1
遍历顺序从前至后,伪代码如下:

vector<int> dp(n+1);
dp[0]=1;dp[1]=1;
for(i=2;i<=n;i++)
dp[i]=dp[i-1]+dp[i-2];
return dp[n];

01背包

不同物品不同价值,背包最多能装的价值。
物品 0 1 2
重量 1 3 4
价值15 20 30 背包最大重量为4
首先可以使用暴力法,通过回溯列举所有可能性,每个物品取或者不取,复杂度为2的n次方。
dp数组含义: 使用动态规划是设置二维数组dp[i][j],代表[0,i]之间的物品,任意取放到容量为j的背包里的最大价值,不放物品i时的最大价值为dp[i-1][j],放了物品i的价值为dp[i-1][j-weight[i]]+value[i]。
递推公式: 解释一下就是根据物品,每次更新对比当前物品放进背包前后价值大小,取最大值,与贪心有点类似,但是贪心只会拿着大的就往包里塞,而动态规划则是边拿边算,如果当前价值更大就把前边放的乱七八糟东西拿出来,递推公式为
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]
dp数组初始化: 二维表格可初始化如下:

ij		0		1		2		4		5 
0		0		15	15	15	15
1		0	
2		0

回顾对dp的定义:在i内任意取物品放到容量为j的价值,表格内现为已知元素,其余需要计算的元素如何定义呢?由递推式可知,每个元素都由上一层元素计算得来,故每次计算都会赋值且在计算之前与其他元素无关,初始化可任意。
遍历顺序:

for(物品){
for(背包容量)
递推公式}

即二维数组先填行数据,该遍历顺序由递推公式决定,因为当前元素要一来上一行元素。

滚动数组

用二维数组来解决01背包是一种比较基础的方法,但是该解空间有太大冗余,其实也可以压缩状态转而采用一维数组。
因为当前层是由上一层推导而来,如果我们将上一行拷贝到当前行,是不是就能将矩阵压缩成一行,而在过程中数组不断更新覆写,好像在向前滚动,故而称为滚动数组。

最长连续递增子序列

含义如题,例如1 3 5 4 7 序列输出3。
dp[i]表示以i为结尾的最长连续递增子序列长度。
因为只看连续,所以只需要比较dp[i]与dp[i-1]的大小即可,递推公式代码表示如下:

if(numb[i]>num[i-1]
dp[i]=dp[i-1]+1;

所有dp初始化为1;
遍历顺序从前向后
for(i=1;i<num,size();i++)
收集结果
if(dp[i]>result) result=dp[i]

动态规划总结

应该是这三个算法里最难的,到现在我也朦朦胧胧,感觉不做个十道八道题确实不能理解,时间所迫现在只能了解个大概,班门弄斧说说自己的想法:
动态规划就是更科学的贪心,通过对前人结果的计算来确定当下最正确的结果,在每个子问题中都得到正确的结果从而在整个问题正确,代码往往很简单,但是借助的dp数组及其含义和递推公式是难点。

简单算法总结

回溯是用递归代替for循环的更暴力的遍历,流程是先判断叶子节点,真则收回数据,否则一层for循环遍历节点引用递归函数实现深度优先遍历,结束后返回上一层。
核心就是深度优先加剪枝,其中剪枝是使回溯高效的关键环节,在for循环内部对节点进行判定后再递归,判断地越早越提前,减掉的枝越大,算法越高效。

贪心思路简单,局部最优推出整体最优,但是使用要注意两点:一是确认当前的局部最优能否推出整体最优;二是贪心的最优到底是谁与谁在何种情况下的最优。
该算法没有固定套路,只能具体问题具体分析,根据不同条件制定不同的贪心策略。

动态规划我也一知半解,斗胆胡言几句,见笑了。个人认为该算法是有根据的贪心,整体结果互相依赖时或者说所谓重叠子问题较多时使用,规模由小到大,层级由下到上,多个简单问题的最优解经过尝试比较出复杂问题的最优解。
实现上需要构造辅助数组来存储子问题的结果,建立辅助数组的递推公式,遍历问题输入集生成dp结果集合。

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

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

相关文章

docker 安装mongodb 实现 数据,日志,配置文件外挂

docker 安装mongodb 实现数据&#xff0c;日志&#xff0c;配置文件外挂 1 背景 最近开发了一个评论系统之前用mysql来存储数据&#xff0c;但是考虑到后期业务增大访问量也会增大&#xff0c;为了兼容这种高并发的场景&#xff0c;因此经过多方面的考虑&#xff0c;我们最终…

调试/抓包工具

一、Fiddler【推荐window使用】 介绍&#xff1a;个人认为是 Windows 平台最好用的抓包工具&#xff1b; 下载&#xff1a;Fiddler | Web Debugging Proxy and Troubleshooting Solutions 使用方式&#xff1a;这一篇文章写的很全&#xff0c;认真看完就够用了 Fiddler 抓包工…

FISCO BCOS 3.0【02】配置和使用系统自带的控制台

官方技术文档&#xff1a;https://fisco-bcos-doc.readthedocs.io/zh-cn/latest/index.html 我们在官方技术文档的基础上&#xff0c;进行&#xff0c;对文档中一些不清楚的地方进行修正 控制台提供了向FISCO BCOS节点部署合约、发起合约调用、查询链状态等功能。 第一步. 安…

Linux本地docker一键部署traefik+内网穿透工具实现远程访问Web UI管理界面

文章目录 前言1. Docker 部署 Trfɪk2. 本地访问traefik测试3. Linux 安装cpolar4. 配置Traefik公网访问地址5. 公网远程访问Traefik6. 固定Traefik公网地址 前言 Trfɪk 是一个云原生的新型的 HTTP 反向代理、负载均衡软件&#xff0c;能轻易的部署微服务。它支持多种后端 (D…

Git详解及 github使用

1.1 关于版本控制 开始之前先看一个没有版本控制的例子 1.1.1 本地版本控制 本地版本控制系统 许多人习惯用复制整个项目目录的方式来保存不同的版本&#xff0c;或许还会改名加上备份时间以示区别。这么做唯一的 好处就是简单&#xff0c;但是特别容易犯错。有时候会混淆所在…

市级奖项+1,持安获「创业北京」创业创新大赛优秀奖!

2274个创业项目参赛 历经五个多月的激烈角逐 第六届“创业北京”创业创新大赛 终于圆满落下帷幕 持安科技在北京市总决赛中再创佳绩&#xff01; 荣获制造业赛道优秀奖 本次大赛由北京市人力资源和社会保障局、北京市发展和改革委员会等11家单位联合主办&#xff0c;以“创…

C语言--从键盘输入10个数字放在数组中,并输出

用scanf读取数字的时候要注意&#xff0c;可以输入一个数字&#xff0c;按一下回车&#xff0c;输入一个数字&#xff0c;按一下回车&#xff0c;也可以一次性输入完10个数据。&#xff08;中间可以用空格隔开&#xff0c;系统会自动识别&#xff09; 输出一:每按下一个数字&am…

数据库mysql详细教学

1024 byte 构成 1 kb 1024 KB > 1MB 1024 MB > 1GB 1024 GB > 1TB 1024 TB > 1PB 内存的数据&#xff0c;断电后会丢失。外存的数据&#xff0c;断电后数据还在~ “持久化” 这样的次&#xff0c;意思就是把数据写到硬盘上。 mysql的第一组基本操作&#xff1a;数…

Linux 系统误将 chmod 权限改成 了 000,如何恢复?

Linux 系统误将 chmod 权限改成 了 000&#xff0c;如何恢复? busybox 是 Linux 标配&#xff0c;含有大多数主流 Linux 命令&#xff0c;你可以把它的存在当作救急备份。简单功能都可以调用 busybox 完成。这也就意味着很多原始命令出故障的情况下都可以用 busybox 暂时替代。…

【js作用域】JavaScript中作用域的是什么?:从编译时其承担什么角色和查询作用域中的变量的角度解析作用域

&#x1f601; 作者简介&#xff1a;一名大四的学生&#xff0c;致力学习前端开发技术 ⭐️个人主页&#xff1a;夜宵饽饽的主页 ❔ 系列专栏&#xff1a;JavaScript进阶指南 &#x1f450;学习格言&#xff1a;成功不是终点&#xff0c;失败也并非末日&#xff0c;最重要的是继…

架构师篇 DDD领域驱动设计篇

一 DDD领域驱动设计 1.1 领域驱动设计 领域驱动设计(英文&#xff1a;Domain-Driven Design&#xff0c;缩写DDD)是一种模型驱动设计的方法&#xff0c;领域驱动设计常以战略设计与战术设计来将整个领域展现的淋漓尽致&#xff0c;其作用范围既面向业务也面向技术。从战略角度…

PHP排序sort()、asort() 和 ksort() 的区别及用法

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

SOLIDWORKS Electrical工程属性配置与图框

导读 很多工程师都是直接使用现有的图框&#xff0c;但是现有图框会遇到一些问题&#xff0c;自己想显示的内容不知道怎么设置出来&#xff0c;或者是图纸显示的内容太繁杂&#xff0c;行列号不符合自己的习惯。这些问题都是关于图框模板的设计。 一、关于工程属性设计的问题…

【每日一题】数位和相等数对的最大和

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;哈希表 写在最后 Tag 【哈希表】【数组】【2023-11-18】 题目来源 2342. 数位和相等数对的最大和 题目解读 在数组中找出数位和相等数对的和的最大值。 解题思路 方法一&#xff1a;哈希表 维护一个不同的数位和表…

es 算法函数 有点不太懂了没有大神给指点一下

我先说一下我对算法分析的理解 算法函数&#xff0c;我们使用算法函数给指定的数据提高对应的值的方式 比如我现在要给一家酒店排名提高排名&#xff0c;我们可以利用算法函数&#xff0c;提高酒店排名&#xff0c;因为酒店的名称 相关算法的使用场景 在使用的时候出现了这…

《循环双向链表》(带哨兵位的头节点)

目录 ​编辑 前言&#xff1a; 关于双向循环带头链表: 模拟实现双向循环带头链表&#xff1a; 1.typedef数据类型 2.打印链表 3.初始化链表&#xff1a; 4.创建节点 5.尾插 6.头插 7.尾删 8.头删 9.寻找节点 10.在节点前插入 11.删除指定节点 单链表和双链表的区别…

Android 解决CameraView叠加2个以上滤镜拍照黑屏的BUG (二)

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…

C++项目案例圆和点的关系 (涉及知识点:头文件定义类,cpp文件实现类,类和作用域,linux编译运行c++项目)

一.项目描述 点与圆有三种关系&#xff1a; 点在圆外 点在圆上 点在圆内计算点到圆心的距离就能判断点在圆的哪个地方。二.项目结构 三.include文件 3.1 Circle类的声明 Circle.h // 防止头文件重复包含 #pragma once // #include<iostream> #include "Point.h&…

PS学习笔记——图层

文章目录 图层面板图层类型新建图层新建方式图层颜色 操作图层修改图层名称选中图层隐藏图层调整图层顺序复制图层 图层面板 按F7可打开/关闭图层面板 该面板就是图层面板了 对所有图层进行筛选的按钮&#xff0c;第一个搜索框可以选择按什么方式进行筛选&#xff0c;支持&am…

Python语言这么火热,其实具有以下特点

Python语言具有以下特点&#xff1a; 简单易学&#xff1a;Python语言是一种解释型语言&#xff0c;语法简单明了&#xff0c;代码简洁&#xff0c;易于理解&#xff0c;可以一边编码一边运行&#xff0c;非常合适编程初学者。门槛较低&#xff1a;Python不需要复杂的环境配置…