动态规划(背包、组合之和...)
1背包问题假设有一个背包体积是 V另外有 n 个物品物品的体积分别是 v1, v2, ... vn每个物品的价值是 w1, w2, ... wn。求怎么将物品放到背包里才能使背包中物品的价值最大 背包问题是一个典型的动态规划问题。动态规划问题中经常包含一个最字比如最大价值 ? 最短路径 ? 动态规划问题的求解思路包括以下几点1只看眼前利益动态规划关键字是动态也就是说结果是在变化的。在计算过程中只看眼前利益只要当前这种情况满足要求那么这就是中间的一个结果。所有情况都遍历完之后的眼前利益就是最终想要的结果。下边的代码是找数组的最大值。FindMax函数中找最大值的时候max 一直是已经遍历的数据的最大值一直在更新体现了只看眼前利益max 也一直在更新直到把数据都遍历完max 就是最终的结果。这就是动态规划。这是动态规划一个最简单的例子。#include iostream int FindMax(int *data, int size) { int max -1; for (int i 0; i 4; i) { if (data[i] max) { max data[i]; } } return max; } int main() { int data[4] {100, 200, 50, 10}; std::cout max: FindMax(data, 4) std::endl; return 0; }2选与不选选与不选就是分类讨论的思想。比如背包问题当考虑一个物品时要考虑两种情况即这个物品放入背包的话最终能放入的最大价值是多少这个物品不放入背包的话最终能放入的价值最大是多少。如果有 n 个物品每个物品都要做这样的分类讨论共有 2 的 n 次方种组合。把这些所有的情况的价值都计算出来哪个组合的价值最大那么这个组合就是最终的结果。排列组合把所有情况都列举出来再找满足条件的结果。3记录历史信息在动态规划的计算过程中对一些情况的讨论往往会重复在计算过程中可以记录历史信息那么可以减小后边的重复计算。背包问题分为两类0-1 背包和无限背包。0-1 背包说的是每个物品的数量只有一个 也就是这个物品要么放进去要么不放进去只有两种情况。无限背包说的是每个物品的数量有无数个每个物品都可以放 0 个1 个或者多个。数据遍历是很多算法的基础。无论是排序算法还是搜索算法或者动态规划。算法的基础就是对一定数量的数据进行遍历在遍历的过程中嵌入自己的算法逻辑逻辑的不同就产生了了不同的算法。数据保存在数据结构中比如数组链表二叉树图。每种数据结构都有自己的遍历方法。1.1 0-1 背包牛客网 01背包链接。01背包使用一维数组时间复杂度是 O(n)使用选与不选的原始算法时间复杂度是 O(2 的 n 次方)。所以优先选用以为数组。1.1.1 一维数组#includeiostream #includevector using namespace std; // 能够放下的最大价值 int MaxValue(std::vectorint v, std::vectorint w, int n, int V) { // 数组的长度是 V 1 // 之所以比背包的体积大 1这样就最大可以使用 V 做数组的下标了便于使用 std::vectorint value(V 1); // 数组元素的值是这个体积下能放下的价值初始化为 0 value.assign(V 1, 0); // 两层循环第一层循环遍历物品 for (int i 0; i n; i) { // 第二层循环遍历背包剩余的空间能放下当前这个物品的空间 // 体积从大到小进行遍历 for (int j V; j v[i]; j--) { // value[j] 是没放这个物品的时候背包在 j 这个体积下的价值 // value[j - v[i]] w[i] 是放下这个物品的时候j 体积下的价值 if (value[j - v[i]] w[i] value[j]) { value[j] value[j - v[i]] w[i]; } } } return value[V]; } // 背包正好装满时的最大价值 int FullMaxValue(std::vectorint v, std::vectorint w, int n, int V) { std::vectorint value(V 1); // 与背包能放下的最大值比较的话 // 初始值是不一样的 // 将 value[0] 初始化为 0 其它的元素初始化为一个非常小的数 // 这个非常小的数要保证物品价值都加起来和这个数相加也不会大于 0 // 这样能保证正好装满的时候value[V] 是大于 0 的 // 最后可以通过 value[V] 是不是大于 0 来判断背包是不是可以正好装满 // 如果能正好装满那么 value[V] 价值是在 value[0] 也就是 0 的基础上加上物品的价值 // 所以value[V] 是大于 0 的。 // 如果不能正好装满那么 value[V] 的价值在计算过程中肯定与一个非常小的数进行了相加 // 所以 value[V] 是小于 0 的 value.assign(V 1, -99999999); value[0] 0; for (int i 0; i n; i) { int tmp_v v[i]; int tmp_w w[i]; for (int j V; j tmp_v; j--) { int value_old value[j]; int value_new value[j - tmp_v] tmp_w; if (value_new value_old) { value[j] value_new; } } } if (value[V] 0) { return 0; } return value[V]; } int main() { int n 0; int V 0; std::vectorint v; std::vectorint w; cin n V; for (int i 0; i n; i) { int a 0; int b 0; cin a b; v.push_back(a); w.push_back(b); } std::cout MaxValue(v, w, n, V) std::endl; std::cout FullMaxValue(v, w, n, V); return 0; }1.1.2 选与不选选与不选使用递归算法。递归算法的时间复杂度是O(2的 n 次方)时间复杂度太大在牛客网上运行经常超时。使用一维数组的方式时间复杂度是 O(n)所以有限选择数组的方式。#includeiostream #includevector using namespace std; // 保存背包能放得下的最大价值 int max_value 0; // 保存背包正好放满时的最大价值 int max_full_value 0; // 已放入的物品的价值 int value 0; // 已放入的物品的体积 int volume 0; void MaxValue(std::vectorint v, std::vectorint w, int n, int V, int index) { if (volume V) { return; } if (index n) { if (volume V value max_value) { max_value value; } if (volume V value max_full_value) { max_full_value value; } return; } for (int i index; i n; i) { // 选择这个物品 volume v[i]; value w[i]; MaxValue(v, w, n, V, i 1); // 不选择这个物品 volume - v[i]; value - w[i]; MaxValue(v, w, n, V, i 1); } } int main() { int n 0; int V 0; std::vectorint v; std::vectorint w; cin n V; for (int i 0; i n; i) { int a 0; int b 0; cin a b; v.push_back(a); w.push_back(b); } MaxValue(v, w, n, V, 0); std::cout max_value std::endl; std::cout max_full_value; return 0; }1.2 无限背包无限背包也叫完全背包牛客网链接如下。完全背包无限背包说的是每个物品的个数都有无限个。可以使用一维数组的方式来求解与 01 背包不同的是在遍历体积的时候需要从小到大进行遍历01 背包是从大到小进行遍历的。为什么从小到大进行遍历呢这样对于一个物品可以遍历到放置多个的情况。比如一个背包的体积是 10一个物品的体积是 2。如果从小到大进行遍历那么只放这个物品的话可以放置 5 个这样的物品体积遍历到 2 的时候可以放一个4 的时候可以再放一个以此类推。如果从大到小进行遍历从 10 遍历到 2那么只能放置一个不能在前边放置的基础之上再次进行放置。#include iostream #include vector using namespace std; int MaxValue(std::vectorint v, std::vectorint w, int n, int V) { std::vectorint value(V 1); value.assign(V 1, 0); for (int i 0; i n; i) { int tmp_v v[i]; int tmp_w w[i]; for (int j tmp_v; j V; j) { int value_old value[j]; int value_new value[j - tmp_v] tmp_w; if (value_new value_old) { value[j] value_new; } } } return value[V]; } int BagFullMaxValue(std::vectorint v, std::vectorint w, int n, int V) { std::vectorint value(V 1); value.assign(V 1, -99999999); value[0] 0; for (int i 0; i n; i) { int tmp_v v[i]; int tmp_w w[i]; for (int j tmp_v; j V; j) { int value_old value[j]; int value_new value[j - tmp_v] tmp_w; if (value_new value_old) { value[j] value_new; } } } if (value[V] 0) { return 0; } return value[V]; } int main() { int n 0; int V 0; std::vectorint v; std::vectorint w; cin n V; for (int i 0; i n; i) { int a 0; int b 0; cin a b; v.push_back(a); w.push_back(b); } int max_value MaxValue(v, w, n, V); int bag_full_max_value BagFullMaxValue(v, w, n, V); std::cout max_value std::endl; std::cout bag_full_max_value std::endl; }2组合之和(数可以复用)39. 组合总和 - 力扣LeetCode看到一个题目我们往往会先在脑子里去想算法过程是什么样的排序、搜索、循环、判断。当想着想着很难想清楚的时候这种时候可以考虑是不是动态规划是不是递归。本题就是一个动态规划的题目而动态规划很多时候又会用到递归算法。class Solution { public: vectorvectorint combinationSum(vectorint candidates, int target) { vectorint one_result; combinationSumHelper(candidates, target, 0, one_result); //参考无限背包问题的循环算法不好实现 // int remain target; // int size candidates.size(); // for (int i 0; i size;) { // if (remain 0) { // ret.push_back(one_result); // } // if (candidates[i] remain) { // //循环算法没有办法在这一个位置做两个选择 // //递归算法可以在这一个地方分两叉 // one_result.push_back(candidates[i]); // } else { // i; // continue; // } // } return ret; } void combinationSumHelper(vectorint candidates, int target_remain, int index, vectorint one_result) { //递归退出条件 if (index candidates.size()) { return; } //递归退出条件 if (target_remain 0) { ret.push_back(one_result); return; } //不选 combinationSumHelper(candidates, target_remain, index 1, one_result); int data candidates[index]; //选 if (data target_remain) { one_result.push_back(data); //每个数可以不限次数使用决定了这里得index就是index不能传index1 combinationSumHelper(candidates, target_remain - data, index, one_result); one_result.pop_back(); } } vectorvectorint ret; };3三角形最小路径和120. 三角形最小路径和 - 力扣LeetCode路径的问题在二叉树、图中也常见到这样的问题。用一个vector来保存路径典型的三段式的递归算法vector.push_back(当前元素)递归vector.pop(当前元素)但是使用这种算法时间复杂度是2的n次方导致运行超时class Solution { public: int minimumTotal(vectorvectorint triangle) { height_ triangle.size(); vectorint path; path.push_back(triangle[0][0]); impl(triangle, 1, 0, path); return min_; } void impl(vectorvectorint triangle, int level, int index, vectorint path) { if (level height_) { int tmp 0; for (auto data : path) { tmp data; } if (tmp min_) { min_ tmp; } return; } path.push_back(triangle[level][index]); impl(triangle, level 1, index, path); path.pop_back(); path.push_back(triangle[level][index 1]); impl(triangle, level 1, index 1, path); path.pop_back(); } int min_ 100000000; int height_ 1; };可以不用path来保存路径因为题目的结果也没有要把路径打印出来在二叉树、图中的路径题中往往是需要将符合目标的路径打印出来这样的题目需要path来保存路径。而当前这个题目不需要将路径打印出来而是求最小的和那么就可以不保存路径而是用一个变量来保存当前的和对应上边三段式中的vector操作就成为了和的加减操作。但是这样的算法时间复杂度仍然是2的n次方仍然会导致运行超时。class Solution { public: int minimumTotal(vectorvectorint triangle) { height_ triangle.size(); return impl(triangle, 0, 0); } int impl(vectorvectorint triangle, int level, int index) { if (level height_) { return 0; } int sum triangle[level][index]; int sum1 impl(triangle, level 1, index); int sum2 impl(triangle, level 1, index 1); if (sum1 sum2) { return sum sum1; } return sum sum2; } int height_ 1; };动态规划不仅仅是只能通过递归算法来实现也可以通过for循环来实现也就是迭代算法。使用二维数组来保存历史状态。两层循环外层循环是遍历数组的层数内存循环是遍历一个一维数组每一层数组的第一个元素和最后一个元素以及中间的元素要分别处理因为第一个元素只受上一层的第一个元素影响最后一个元素只收上一层的最后一个元素影响中间的元素受上一层的同等下标的元素以及前一个下标 的元素影响。class Solution { public: int minimumTotal(vectorvectorint triangle) { int height triangle.size(); vectorvectorint sum(height, vectorint(height,0)); sum[0][0] triangle[0][0]; for (int level 1; level height; level) { for (int i 0; i level; i) { if (i 0) { sum[level][i] sum[level - 1][i] triangle[level][i]; } else if (i level) { sum[level][i] sum[level - 1][i - 1] triangle[level][i]; } else { sum[level][i] sum[level - 1][i] sum[level - 1][i - 1] ? sum[level - 1][i] triangle[level][i] : sum[level - 1][i - 1] triangle[level][i]; } } } int ret 100000000; for (auto tmp : sum[height - 1]) { if (tmp ret) { ret tmp; } } return ret; } };类似于背包问题中可以将二维数组优化为一维数组来实现本题目也可以将二维数组进行优化。使用一维数组之后对于每一层的数据进行遍历要从大到小进行遍历。从大到小进行遍历的时候使用的元素值才是上一层的结果而从小到大进行遍历那么上一层的数据会被覆盖。class Solution { public: int minimumTotal(vectorvectorint triangle) { int height triangle.size(); vectorint sum(height, 0); sum[0] triangle[0][0]; for (int level 1; level height; level) { for (int i level; i 0; i--) { if (i 0) { sum[i] sum[i] triangle[level][i]; } else if (i level) { sum[i] sum[i - 1] triangle[level][i]; } else { sum[i] sum[i] sum[i - 1] ? sum[i] triangle[level][i] : sum[i - 1] triangle[level][i]; } } } int ret 100000000; for (auto tmp : sum) { if (tmp ret) { ret tmp; } } return ret; } };

相关新闻

Unity 2021.3.6配置Pico SDK 230实现手势识别完整指南

Unity 2021.3.6配置Pico SDK 230实现手势识别完整指南

1. 项目概述:为什么Pico 4手势识别值得投入?如果你正在用Unity为Pico 4开发应用,并且对手势识别这个功能点产生了兴趣,那这篇文章就是为你准备的。我最近刚完成了一个基于Pico 4的手势交互项目,从环境搭建到功能上线&a…

2026/8/21 17:02:44 阅读更多 →
Hyperf框架实战:构建高性能PHP微服务应用

Hyperf框架实战:构建高性能PHP微服务应用

1. Hyperf框架入门:从零构建高性能PHP应用第一次接触Hyperf时,我被它的性能数据震撼到了——单机百万级QPS的处理能力,这完全颠覆了我对PHP框架的认知。作为一个长期使用Laravel的开发者,Hyperf带来的协程和依赖注入设计让我看到了…

2026/9/3 19:41:40 阅读更多 →
UE性能优化全攻略:从CPU/GPU瓶颈分析到移动端专项优化

UE性能优化全攻略:从CPU/GPU瓶颈分析到移动端专项优化

1. 项目概述:为什么UE性能优化是开发者的必修课刚接触Unreal Engine(虚幻引擎)的新手,往往会被其强大的画面表现力和蓝图可视化编程所吸引,一头扎进场景搭建和功能实现中。然而,当项目规模逐渐扩大&#xf…

2026/9/3 18:59:29 阅读更多 →

最新新闻

PHP+AJAX+MySQL实战:从零构建网络象棋对弈系统

PHP+AJAX+MySQL实战:从零构建网络象棋对弈系统

简介:这是一套基于Web技术实现的在线双人对战象棋系统,面向PHP初学者与Web全栈学习者,解决传统单机棋类游戏无法联网互动的问题,适用于课程设计、毕业项目或Web交互实践。资源共231个文件,包含156个PHP后端逻辑文件&am…

2026/9/4 4:57:13 阅读更多 →
Python做职业自动化工程师实战教程

Python做职业自动化工程师实战教程

唐宇迪同济大学硕士,华东理工大学博士《跟着迪哥学数据分析与机器学习实战》这本书的作者, 对机器学习算法颇为精通, 其主攻方向为计算机视觉, 身为联通, 移动, 中信等公司特邀的企业培训导师, 已累计开发五十余门课程, 这些课程覆盖了人工智能的热门方向。高洛峰前…

2026/9/4 4:57:13 阅读更多 →
电机控制核心技术解析:FOC算法、STM32开发与高薪就业指南

电机控制核心技术解析:FOC算法、STM32开发与高薪就业指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/4 4:57:13 阅读更多 →
基于Vue的校园闲置交易平台H5前端架构与核心功能实现

基于Vue的校园闲置交易平台H5前端架构与核心功能实现

简介:本资源是一款面向高校师生的校园闲置物品交易平台H5用户端完整前端源码,基于Vue 2/3主流技术栈开发,聚焦移动端二手交易场景,解决校内物品流转低效、信息分散、平台缺失等实际问题,适合前端初学者进阶实践与课程设…

2026/9/4 4:57:13 阅读更多 →
RINEX导航文件解析:从readRinexNav函数到卫星位置计算的完整指南

RINEX导航文件解析:从readRinexNav函数到卫星位置计算的完整指南

简介:本资源是一个面向GNSS高精度定位研究者与MATLAB开发者的轻量级导航星历解析工具,专用于读取RINEX格式导航文件并提取卫星轨道参数与钟差信息,解决GPS、北斗等多系统星历数据解析这一基础但关键的技术需求。压缩包仅含1个核心MATLAB脚本&…

2026/9/4 4:57:13 阅读更多 →
从Hatch到Muse:Meta新品发布背后的内部代号与候补名单机制

从Hatch到Muse:Meta新品发布背后的内部代号与候补名单机制

Meta 内部项目 Hatch 将以 Muse 之名发布,同时开放候补名单。这个新闻单看只有一句话,但它背后涉及三个值得拆解的工程问题:Meta 内部项目为什么往往以代号孵化、正式对外时又要换一个独立品牌名;一个还没有公开下载入口的硬件或软…

2026/9/4 4:56:12 阅读更多 →

日新闻

ESP32S2嵌入式收音机全栈开发实战指南

ESP32S2嵌入式收音机全栈开发实战指南

简介:本资源是一个基于ESP32-S2芯片的嵌入式综合实践项目,面向本科毕业设计、课程设计及实训开发人员,聚焦网络收音机与FM收音机双模功能实现,融合ESP-IDF框架、ESP-ADF音频开发库与LVGL图形界面库,具备完整软硬件协同…

2026/9/4 0:00:28 阅读更多 →
WorkBuddy+Python实战:从零搭建商品库存管理系统

WorkBuddy+Python实战:从零搭建商品库存管理系统

最近想自己动手做一个“商品库存管理系统”的人变多了。很多开网店、做小团队ERP选型、或者刚学Python的读者,不是不想用系统,而是被传统开发路径劝退了:要装数据库,要写后端接口,要学前端页面,还要考虑多人…

2026/9/4 0:00:28 阅读更多 →
旅游情感分析:基于Python的垂直场景深度解析

旅游情感分析:基于Python的垂直场景深度解析

简介:本资源是一份面向计算机专业本科生的毕业设计实践项目,聚焦旅游行业真实场景,解决旅游平台对用户评论情感倾向自动识别与管理的需求。系统基于Python 3.9.11与Anaconda环境构建,集成携程、马蜂窝双平台爬虫模块,并…

2026/9/4 0:00:28 阅读更多 →

周新闻

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析

每年校招季我都会接触不少准备数据库方向笔试的同学,看到最多的状态就是:简历上写着“熟悉 MySQL”“了解索引优化”,一碰到数据库管理工程师的笔试卷,却在索引、事务、锁、备份恢复这些题目上翻车。网易这套 2018 校园招聘数据库…

2026/9/3 4:22:22 阅读更多 →
数字电路时序基石:深入理解建立时间与保持时间

数字电路时序基石:深入理解建立时间与保持时间

1. 这不是“背公式”的事:时间参数到底在约束什么你翻过数字电路教材,一定见过这两个词:建立时间(Setup Time)和保持时间(Hold Time)。它们常被并列写在触发器(Flip-Flop&#xff09…

2026/9/3 4:22:01 阅读更多 →
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

1. 项目缘起:从赛题到超声波测距机的诞生第八届蓝桥杯单片机设计与开发国赛的题目,我至今记忆犹新。它没有直接给出一个花哨的名字,而是用“超声波测距机”这个朴实无华的功能描述,精准地勾勒出了考核的核心。对于当时备赛的我而言…

2026/9/3 4:22:59 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/3 4:17:49 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/3 4:18:56 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/3 4:21:44 阅读更多 →