动态规划算法解决背包问题,算法分析与C语言代码实现,时间效率解析

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: 动态规划算法解决背包问题,算法分析与C语言代码实现,时间效率解析

目录


动态规划算法

算法介绍与思想

例子理解:斐波那契数

背包问题

问题介绍

算法思路

时间效率分析

代码实现


正文


动态规划算法


算法介绍与思想


     动态规划(dynamic programming)是一种算法设计技术,它有着相当有趣的历史。作为一种使多阶段决策过程最优的通用方法,它是在20世纪50年代由一位卓越的美国数学家理查德·贝尔曼(Richard Bellman)发明的。因此,这个技术名字中的“programming”是计划和规划的意思,不是代表计算机中的编程。它不仅是应用数学中用来解决某类最优问题的重要工具,而且还在计算机领域中被当作一种通用的算法设计技术来使用。在这里,我们正是从这个角度来考虑这种技术的。


       如果问题是由交叠的子问题构成的,我们就可以用动态规划技术来解决它。一般来说,这样的子问题出现在对给定问题求解的递推关系中,这个递推关系中包含了相同类型的更小子问题的解。动态规划法建议,与其对交叠的子问题一次又一次地求解,还不如对每个较小的子问题只求解一次并把结果记在表中,这样就可以从表中得出原始问题的解。


例子理解:斐波那契数


       斐波那契数。我们可以发现它对这项技术做出了很好的阐述。斐波那契数是以下序列中的元素:

1 ,1 ,2 ,3 ,5 ,8 ,13 ,21 ,34 ,...... ,

     它可以用一个简单的递推式和两个初始条件来定义:


      如果我们试图利用第一个递推式直接计算第n个斐波那契数F(n),可能必须对该函数的相同值重新计算好几遍。请注意,计算Fn)这个问题是以计算它的两个更小的交叠子问题F(n-1)和F(n -2)的形式来表达的。所以,我们可以简单地在一张一维表中填入n+1个Fn)的连续值。开始时,通过观察初始条件第二个递推式可以填入0和l,然后以式第一个递推式作为运算规则计算出其他所有的元素。显然,该数组的最后一个元素应该包含F(n)。这个非常简单的算法只需要一个单循环就能完成。


       请注意,实际上,如果只存储斐波那契序列中最后两个元素的值,就可以避免使用额外的数组来完成这个任务。这种现象并不罕见,而且我们会在本章中遇到更多这样的例子。虽然动态规划法的直接应用也可以解释成一种特殊类型的空间换时间权衡技术,但有时候一个动态规划算法经过改进可以避免使用额外的空间。


       某些算法无需计算出该序列前面所有的元素就可以给出第n个斐波那契数的值。然而,一般来说,一个算法如果基于经典的从底至上动态规划方法,那就需要解出给定问题的所有较小子问题。动态规划法的一个变化形式试图避免对不必要的子问题求解。


       但无论我们使用动态规划的经典的从底至上版本还是它基于记忆功能的从顶至下版本,设计这样一种算法的关键步骤还是相同的,即导出一个问题实例的递推关系,该递推关系包含该问题的更小(并且是交叠的)子实例的解。但像计算第n个斐波那契数这样,直接表现为公式第一个递推式的形式,可以说是这个规则的一个极少例外。


       由于动态规划的大多数应用都是求解最优化问题,因此我们需要指出这类应用中的一个一般性法则。理查德·贝尔曼称其为最优化法则(principle of optimality)。该法则认为最优化问题任一实例的最优解,都是由其子实例的最优解构成的。最优化法则在大多数情况下是成立的,尽管也有少数情况例外(一个相当罕见的例子,就是在图中找最长简单路径)。虽然在应用动态规划求解具体问题时,需要检查最优化法则是否适用,但在设计动态规划算法时,做一个这样的检查并不困难。


背包问题


问题介绍


       背包问题是一种组合优化的NP完全问题1..也就是说,没有多项式时间复杂度的解法。背包问题的基本形式是:给定一组物品,每种物品都有自己的重量和价值,在限定的总重量内,我们如何选择,才能使得物品的总价值最高。


       根据物品是否可以重复选择,背包问题可以分为以下几种类型:


01背包问题:每种物品只有一个,可以选择放或不放。

完全背包问题:每种物品有无限个,可以任意选择放或不放。

多重背包问题:每种物品有有限个,可以选择放或不放。

       此外,还有—些其他的变形,例如恰好装满、求方案总数、求所有的方案等。


算法思路


       我们用设计一个背包问题的动态规划算法来作为本节的开始:给定n个重量为w1,…",wn,价值为v1,…, vn的物品和一个承重量为W的背包,求这些物品中最有价值的一个子集,并且要能够装到背包中。在这里假设所有的重量和背包的承重量都是正整数,而物品的数量不必是整数。


       为了设计一个动态规划算法,需要推导出一个递推关系,用较小子实例的解的形式来表示背包问题的实例的解。让我们来考虑一个由前i个物品(1≤i≤n )定义的实例,物品的重量分别为w1,…, wi,价值分别为v1,…, vi,背包的承重量为j(1≤j≤W)。设F(i,j)为该实例的最优解的物品总价值,也就是说,是能够放进承重量为j的背包中的前i个物品中最有价值子集的总价值。可以把前i个物品中能够放进承重量为j的背包中的子集分成两个类别:包括第 i个物品的子集和不包括第i个物品的子集。然后有下面的结论:


       (1)根据定义,在不包括第i个物品的子集中,最优子集的价值是F(i-1,j)。


       (2)在包括第i个物品的子集中(因此,j-wi≥0),最优子集是由该物品和前i-1个物品中能够放进承重量为j- w;的背包的最优子集组成。这种最优子集的总价值等于vi+F(i- 1,j - wi)。


       因此,在前i个物品中最优解的总价值等于这两个价值中的较大值。当然,如果第i个物品不能放进背包,从前i个物品中选出的最优子集的总价值等于从前i-1个物品中选出的最优子集的总价值。这个结果导致了下面这个递推式:


       我们可以很容易地如下定义初始条件:


       当j≥0时,F(0,j)= 0;当i≥0时,F(i, 0)=0


       我们的目标是求F(n, W),即n个给定物品中能够放进承重量为W的背包的子集的最大总价值以及最优子集本身。


       下图给出了涉及公式上面的递推式和公式初始条件的物品总价值。当i, j >0时,为了计算第i行第j列的单元格F(i,j),我们拿前一行同一列的单元格与vi加上前一行左边wi列的单元格的和做比较,计算出两者的较大值。这个表格既可以逐行填,也可以逐列填。


时间效率分析


       该算法的时间效率和空间效率都属于0(nW)。用来求最优解的具体组成的时间效率属于O(n)。


代码实现


#include <stdio.h>
#define max(a,b) ((a)>(b)?(a):(b))
// n: 物品个数
// w: 物品重量数组
// v: 物品价值数组
// C: 背包容量
// 返回: 最大总价值
int knapsack(int n, int w[], int v[], int C) {
  // dp[j] 表示容量为j的背包的最大价值
  int dp[C+1];
  // 初始化边界条件
  for (int j = 0; j <= C; j++) {
    dp[j] = 0; // 没有物品可选,价值为0
  }
  // 状态转移方程
  for (int i = 1; i <= n; i++) {
    for (int j = C; j >= w[i-1]; j--) { // 倒序遍历,避免覆盖之前的状态
      // 在放入和不放入第i个物品中选择最大价值
      dp[j] = max(dp[j], dp[j-w[i-1]] + v[i-1]);
    }
  }
  // 返回最终结果
  return dp[C];
}
int main() {
  // 测试数据
  int n = 4;
  int w[] = {2,3,4,5};    //测试用例
  int v[] = {3,4,5,6};
  int C = 8;
  // 调用函数
  int ans = knapsack(n,w,v,C);
  // 输出结果
  printf("The maximum value is %d\n", ans);
  return 0;
}


相关文章
|
29天前
|
数据采集 自然语言处理 搜索推荐
基于qwen2.5的长文本解析、数据预测与趋势分析、代码生成能力赋能esg报告分析
Qwen2.5是一款强大的生成式预训练语言模型,擅长自然语言理解和生成,支持长文本解析、数据预测、代码生成等复杂任务。Qwen-Long作为其变体,专为长上下文场景优化,适用于大型文档处理、知识图谱构建等。Qwen2.5在ESG报告解析、多Agent协作、数学模型生成等方面表现出色,提供灵活且高效的解决方案。
136 49
|
19天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
21天前
|
测试技术 开发者 Python
使用Python解析和分析源代码
本文介绍了如何使用Python的`ast`模块解析和分析Python源代码,包括安装准备、解析源代码、分析抽象语法树(AST)等步骤,展示了通过自定义`NodeVisitor`类遍历AST并提取信息的方法,为代码质量提升和自动化工具开发提供基础。
34 8
|
18天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
38 4
|
17天前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
18 1
|
20天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
49 1
|
25天前
|
机器学习/深度学习 存储 人工智能
强化学习与深度强化学习:深入解析与代码实现
本书《强化学习与深度强化学习:深入解析与代码实现》系统地介绍了强化学习的基本概念、经典算法及其在深度学习框架下的应用。从强化学习的基础理论出发,逐步深入到Q学习、SARSA等经典算法,再到DQN、Actor-Critic等深度强化学习方法,结合Python代码示例,帮助读者理解并实践这些先进的算法。书中还探讨了强化学习在无人驾驶、游戏AI等领域的应用及面临的挑战,为读者提供了丰富的理论知识和实战经验。
50 5
|
28天前
|
存储 缓存 算法
通过优化算法和代码结构来提升易语言程序的执行效率
通过优化算法和代码结构来提升易语言程序的执行效率
|
1月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
29天前
|
数据采集 存储 自然语言处理
基于Qwen2.5的大规模ESG数据解析与趋势分析多Agent系统设计
2022年中国上市企业ESG报告数据集,涵盖制造、能源、金融、科技等行业,通过Qwen2.5大模型实现报告自动收集、解析、清洗及可视化生成,支持单/多Agent场景,大幅提升ESG数据分析效率与自动化水平。
102 0

推荐镜像

更多
下一篇
DataWorks