贪心算法:部分背包问题深度解析

简介:

该Java代码基于贪心算法实现了分数背包问题的求解,核心通过单位价值降序排序和分阶段装入策略实现最优解。首先对Product数组执行双重循环冒泡排序,按wm(价值/重量比)从高到低重新排列物品;随后分两阶段装入:循环完整装入单位价值最高的物品直至剩余容量不足,最后计算部分装入比例并累加残值。代码突出展现了贪心算法的"局部最优推导全局最优"特性,通过预排序确保每次选择当前最优物品,其O(n²)排序过程与线性装入流程共同构成算法主体,物品装入状态数组和DecimalFormat精度控制体现实用性。(注:当前排序逻辑存在i,j均从1开始遍历的非常规冒泡实现,可能需优化为标准冒泡或更高效的排序算法)

贪心算法(Greedy Algorithm)

定义:一种在每一步选择中都采取当前状态下最优(即最有利)的决策,从而希望导致结果是全局最优的算法策略。

核心特征

  1. 局部最优性:每个决策步骤都选择当前最佳选项
  2. 不可回溯性:做出的选择不可更改
  3. 高效性:通常时间复杂度低于动态规划

适用条件

  • 问题具有最优子结构(全局最优包含局部最优)
  • 问题具有贪心选择性质(局部最优能推导全局最优)

典型应用场景

  1. 部分背包问题(当前案例)
  2. 霍夫曼编码
  3. 最小生成树(Prim/Kruskal算法)
  4. 最短路径(Dijkstra算法)

在部分背包中的体现

// 按单位价值降序排列(核心贪心策略)
if(products[i].wm > products[j].wm) { ... }

优缺点对比

优势局限性
时间复杂度低(O(n²))不能保证所有问题的最优解
实现简单直观依赖问题特性(需证明正确性)
空间复杂度低(O(n))选择策略设计难度较高

题目:

注意:题目中的排序方法使用的是:归并排序

一、核心代码逐行解析

1. 数据结构定义(Product类)

class Product {int w;    // 物品实际重量int v;    // 物品完整价值double wm; // 单位重量价值(v/w)public Product(int w, int v) {this.w = w;this.v = v;// 计算单位价值(保留两位小数)this.wm = Double.parseDouble(new DecimalFormat("#.00").format((double)v/w));// 处理边界条件if(w == 0 || v == 0) this.wm = 0;}
}

执行流程说明

  • w=40, v=40wm=1.00
  • w=50, v=60wm=1.20
  • w=20, v=30wm=1.50

2. 核心算法实现

// 阶段1:完整物品装入
int i = 1;
while(W >= products[i].w) {  // 剩余容量 >= 当前物品重量weight += products[i].w;  // 累加已装重量result += products[i].v;  // 累加总价值W -= products[i].w;       // 更新剩余容量items[i] = 1;             // 标记完全装入i++;                      // 指向下个物品
}// 阶段2:部分物品装入
result += products[i].wm * W;   // 按比例计算剩余价值
items[i] = (double)W/products[i].w; // 记录装入比例

执行示例
初始容量W=100:

  1. 装入物品3(w=20)→ W=80
  2. 装入物品5(w=30)→ W=50
  3. 装入物品2(w=50)→ W=0
    最终剩余容量处理:无

二、算法流程图解

完整执行流程

graph TDA[初始化物品数据] --> B[计算单位价值wm]B --> C{排序检查}C -->|i=1| D[比较products[i]与products[j]]D -->|wm更大| E[交换物品位置]E --> F{完成排序?}F -->|否| CF -->|是| G[循环装入完整物品]G --> H{容量足够?}H -->|是| I[完整装入]H -->|否| J[计算部分装入]J --> K[输出结果]

时间复杂度分解

三、关键算法深度解析

1. 排序算法实现

public static void sortProducts(Product[] products, int N) {for(int i=1; i<=N; i++) {        // 控制排序轮次for(int j=1; j<=N; j++) {    // 执行元素比较if(products[i].wm > products[j].wm) {// 元素交换三部曲Product temp = products[j];products[j] = products[i];products[i] = temp;}}}
}

算法特点

  • 典型冒泡排序变种
  • 时间复杂度:严格O(n²)
  • 空间复杂度:O(1)原地排序
  • 缺陷:存在冗余比较(每次全量遍历)
优化代码
// 优化后的冒泡排序实现
public static void sortProducts(Product[] products, int N) {for(int i = 1; i <= N-1; i++) { // 只需N-1轮比较boolean swapped = false;    // 交换标志位for(int j = 1; j <= N-i; j++) { // 每轮减少比较范围if(products[j].wm < products[j+1].wm) { // 比较相邻元素Product temp = products[j];products[j] = products[j+1];products[j+1] = temp;swapped = true;}}if(!swapped) break; // 提前终止优化}
}

2. 贪心策略实现

// 物品选择数组初始化
double[] items = new double[N+1]; // 索引1~N存储选择比例// 完整物品标记
items[i] = 1; // 二进制式标记(0或1)// 部分物品计算
double ratio = (double)W/products[i].w; // 精确计算比例

数学原理
总价值 = Σ(完整物品v) + 剩余容量×max(wm)

四、完整实例代码

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Properties;public class Test2 {public static void main(String[] args) {int N=5; // 总数量int W=100;// 总容量double result =0.0;// 总价值double[] items =new double[N+1];Product[] products=new Product[]{new Product(0,0),new Product(40,40),new Product(50,60),new Product(20,30),new Product(10,20),new Product(30,65),};int weight =0;sortProducts(products,N);printProducts(products,N);int i =1;while(W>=products[i].w){weight+=products[i].w;result+=products[i].v;W -= products[i].w;items[i]=1;i++;}// 部分result+=products[i].wm*W;items[i]=(double) W/products[i].w;System.out.println(result);printItems(items,N);}
//    // 排序
//    public  static void sortProducts(Product[] products,int N) {
//        for(int i=1;i<=N;i++){
//            for( int j=1;j<=N;j++){
//                if(products[i].wm>products[j].wm){
//                    Product product=products[j];
//                    products[j]=products[i];
//                    products[i]=product;
//                }
//            }
//        }
//    }// 优化后的冒泡排序实现public static void sortProducts(Product[] products, int N) {for(int i = 1; i <= N-1; i++) { // 只需N-1轮比较boolean swapped = false;    // 交换标志位for(int j = 1; j <= N-i; j++) { // 每轮减少比较范围if(products[j].wm < products[j+1].wm) { // 比较相邻元素Product temp = products[j];products[j] = products[j+1];products[j+1] = temp;swapped = true;}}if(!swapped) break; // 提前终止优化}}public static void printProducts(Product[] pducts,int N){for (int i = 1; i <=N ; i++) {System.out.println(pducts[i]);}}public static void printItems(double[] items,int N){for (int i = 1; i <=N ; i++) {System.out.print(items[i]+" ");}System.out.println("");}}class Product{int w;// 重量int v;// 价值double wm;// 重量价值public Product(int w, int v) {this.w = w;this.v = v;this.wm =Double.parseDouble(new DecimalFormat("#.00").format((double) this.v/this.w));if(w==0 ||  v==0){this.wm =0;}}public Product(){}@Overridepublic String toString() {return "Product{" +"w=" + w +", v=" + v +", wm=" + wm +'}';}
}

运行结果:

五、复杂度分析进阶

时间复杂度对比

排序算法时间复杂度本实现采用适用场景
冒泡排序O(n²)教学示例
快速排序O(n logn)生产环境
堆排序O(n logn)大数据量

空间复杂度优化

// 原始实现
Product[] products = new Product[N+1]; // 空间O(n)// 优化建议
List<Product> productList = new ArrayList<>(); // 动态空间管理

六、代码缺陷与改进

现存问题

  1. 数组越界风险
// 当i超过N时访问products[i]会导致异常
while(W >= products[i].w) // 需添加i <= N条件判断
  1. 精度丢失问题
// double计算存在精度误差
new DecimalFormat("#.00").format(...) // 建议改用BigDecimal

改进方案

// 优化后的排序实现(使用Java内置排序)
Arrays.sort(products, 1, N+1, (a,b) -> Double.compare(b.wm, a.wm));// 优化后的容量检查
while(i <= N && W >= products[i].w) {// ...原有逻辑
}

七、应用场景扩展

实际应用案例

  1. 货物装载优化:海运集装箱的货物配载
  2. 资源分配:云计算中的资源分配策略
  3. 投资组合:金融资产的部分投资

性能测试数据

物品规模冒泡排序耗时快速排序耗时
1002ms0.3ms
1000150ms1.2ms
1000015s5ms

八、算法扩展思考

动态规划对比

特性贪心算法动态规划
时间复杂度O(n²)O(nW)
空间复杂度O(n)O(nW)
解的质量最优最优
适用场景可分割物品不可分割

多约束扩展

当存在多维约束(体积+重量)时,可引入:

max Σ(v_i*x_i) 
s.t.
Σ(w_i*x_i) ≤ W 
Σ(v_i*x_i) ≤ V 
0 ≤ x_i ≤ 1

九、总结与展望

本实现完整展示了贪心算法在部分背包问题中的应用,核心在于:

  1. 正确计算单位价值
  2. 有效排序策略
  3. 分阶段装入逻辑

虽然当前实现存在时间复杂度较高的瓶颈,但通过:

  • 改进排序算法
  • 增加边界检查
  • 提升计算精度
    可将其升级为生产级解决方案。该算法在物流优化、金融投资等领域具有重要实践价值。

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

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

相关文章

13. Langchain异步处理:提升应用性能的关键技巧

引言&#xff1a;从"顺序等待"到"并行加速" 2025年某电商平台引入LangChain异步处理后&#xff0c;大促期间订单处理能力提升5倍&#xff0c;系统响应延迟降低70%。本文将基于LangChain的异步架构&#xff0c;详解如何通过并行执行流式处理&#xff0c;让…

ros2-rviz2控制unity仿真的6关节机械臂,探索从仿真到实际应用的过程

文章目录 前言&#xff08;Introduction&#xff09;搭建开发环境&#xff08;Setup Development Environment&#xff09;在window中安装Unity&#xff08;Install Unity in window&#xff09;创建Docker容器&#xff0c;并安装相关软件&#xff08;Create Docker containers…

计算机组成原理笔记(十四)——3.4指令类型

一台计算机的指令系统可以有上百条指令&#xff0c;这些指令按其功能可以分成几种类型&#xff0c;下面分别介绍。 3.4.1数据传送类指令 一、核心概念与功能定位 数据传送类指令是计算机指令系统中最基础的指令类型&#xff0c;负责在 寄存器、主存、I/O设备 之间高效复制数…

各开源协议一览

在 GitHub 上&#xff0c;开源项目通常会使用一些常见的开源协议来定义项目的使用、修改和分发规则。以下是目前 GitHub 上最常见的几种开源协议及其差异和示例说明&#xff1a; TL;DR 协议宽松程度是否强制开源专利保护适用场景MIT最宽松否无希望代码被广泛使用Apache 2.0宽松…

51c自动驾驶~合集17

我自己的原文哦~ https://blog.51cto.com/whaosoft/13793157 #汇聚感知、定位、规划控制的自动驾驶系统 自动驾驶技术在应用到车辆上之后可以通过提高吞吐量来缓解道路拥堵&#xff0c;通过消除人为错误来提高道路安全性&#xff0c;并减轻驾驶员的驾驶负担&#xff0c;从…

小程序开发指南

小程序开发指南 目录 1. 小程序开发概述 1.1 什么是小程序1.2 小程序的优势1.3 小程序的发展历程 2. 开发准备工作 2.1 选择开发平台2.2 开发环境搭建2.3 开发模式选择 3. 小程序开发流程 3.1 项目规划3.2 界面设计3.3 代码开发3.4 基本开发示例3.5 数据存储3.6 网络请求3.7 …

Day15:关于MySQL的编程技术——基础知识

前言&#xff1a;先创建一个练习的数据库和数据 1.创建数据库并创建数据表的基本结构 -- 创建练习数据库 CREATE DATABASE db_programming; USE db_programming;-- 创建员工表&#xff08;包含各种数据类型&#xff09; CREATE TABLE employees (emp_id INT PRIMARY KEY AUTO…

批处理脚本bat丨遍历一个包含项目名称的数组,并对每个文件中的项目执行 git pull 操作 (一键拉很多文件的代码)

文章目录 前言一、操作方式二、文件展示三、分析代码结构四、代码五、需要注意的潜在问题六、改进后的代码七、改进说明八、感谢 前言 由于之前git服务部署在本地服务器&#xff0c;处于代码安全角度考虑。领导让我将所有的项目代码手动物理备份一份并且发给他。 这种傻傻的操…

【C++】C与C++、C++内存空间、堆与栈

C嘎嘎嘎嘎嘎~ C与C的区别与联系 C内存空间 int global_var; // 未初始化全局变量&#xff0c;BSS段 const char* str "Hello"; // 字符串常量text段 in数据段void func() {static int static_var; // 未初始化的静态变量&#xff0c;数据段int local_var; …

舵机:机器人领域的“关节革命者”

机器人的技术&#xff0c;每一个细微的进步都可能引领一场行业变革。而在这场变革中&#xff0c;舵机作为机器人关节的核心部件&#xff0c;正悄然上演着一场革命性的应用风暴。从简单的关节运动到复杂的姿态控制&#xff0c;舵机以其卓越的性能和无限的可能&#xff0c;重新定…

微前端的不断探索之路—— qiankun 实战与思考!

全文目录&#xff1a; 开篇语&#x1f4dd; 前言&#x1f6e0;️ 微前端是什么&#xff1f;为什么需要它&#xff1f;&#x1f4a1; 先从“前端痛点”说起&#x1f9d0; 微前端的优势 &#x1f939;‍♀️ qiankun 简介与核心概念&#x1f31f; 为什么选择 qiankun&#xff1f;…

拆解加密黑盒

在Web安全与数据爬取领域&#xff0c;JavaScript加密黑盒的逆向工程是核心技术之一。本文基于行业通用方法论与实战案例&#xff0c;提炼出一套标准化的五步逆向流程&#xff0c;涵盖目标定位、代码提取、逻辑分析、算法复现到自动化集成的全链路解决方案&#xff0c;帮助开发者…

IntelliJ IDEA 中安装和使用通义灵码 AI 编程助手教程

随着人工智能技术的发展&#xff0c;AI 编程助手逐渐成为提升开发效率的强大工具。通义灵码是阿里云推出的一款 AI 编程助手&#xff0c;它能够帮助开发者实现智能代码补全、代码解释、生成单元测试等功能&#xff0c;极大地提升了编程效率和代码质量。 IntelliJ IDEA 是一款广…

Redis 特性和应用场景

1. Redis特性 1&#xff09;In-memory data structures Redis 在内存中存储数据&#xff0c;key 是 String&#xff0c; value 可以是 hash, list, set, sorted set, stream ... MySQL主要是通过 “表” 的方式来存储组织数据的 “关系型数据库” Redis主要是通过 “键值对”…

每天五分钟深度学习:非线性激活函数的导数

本文重点 本文探讨了神经网络中几种常见非线性激活函数(Sigmoid、Tanh、ReLU、Leaky ReLU、ELU、Softmax)的导数特性。通过对各激活函数导数的数学推导与实际应用分析,揭示了不同激活函数在梯度传播、收敛速度及模型表达能力方面的差异。研究发现,ReLU及其变体在计算效率与…

redis哨兵机制 和集群有什么区别:

主从&#xff1a; 包括一个master节点 和多个slave节点&#xff1a; master节点负责数据的读写&#xff0c;slave节点负责数据的读取&#xff0c;master节点收到数据变更&#xff0c;会同步到slave节点 去实现数据的同步。通过这样一个架构可以去实现redis的一个读写分离。提升…

关于读完《毛泽东选集》的一些思考迭代

看完毛选前四卷&#xff0c;从革命初期一直讲到抗战胜利&#xff0c;共75.8W字&#xff0c;花费67个小时读完。从1925年发表的“中国社会各阶级的分析”&#xff0c;跨越100年&#xff0c;通过67个小时向主席学习到&#xff1a; 实事求是 从实践中来再到实践中去 用辩证与发展…

MySQL——MVCC(多版本并发控制)

目录 1.MVCC多版本并发控制的一些基本概念 MVCC实现原理 记录中的隐藏字段 undo log undo log 版本链 ReadView 数据访问规则 具体实现逻辑 总结 1.MVCC多版本并发控制的一些基本概念 当前读&#xff1a;该取的是记录的最新版本&#xff0c;读取时还要保证其他并发事务…

【Linux篇】深入理解文件系统:从基础概念到 ext2 文件系统的应用与解析

文件系统的魔法&#xff1a;让计算机理解并存储你的数据 一. 文件系统1.1 块1.2 分区1.3 inode(索引节点) 二. ext2文件系统2.1 认识文件系统2.2 Block Group (块组)2.2.1 Block Group 的基本概念2.2.2 Block Group 的作用 2.3 块组内部结构2.3.1 超级块&#xff08;Super Bloc…

3 VS Code 配置优化与实用插件推荐:settings.json 详解、CodeGeeX 智能编程助手及插件离线安装方法

1 优化 settings.json 文件 1.1 settings.json 简介 settings.json 是 VS Code 的核心配置文件&#xff0c;用于存储用户的个性化设置和偏好。通过该文件&#xff0c;用户可以自定义和覆盖 VS Code 的默认行为&#xff0c;包括但不限于以下方面&#xff1a; 编辑器外观&#…