贪心算法应用:在线租赁问题详解

article/2025/7/13 22:07:48

在这里插入图片描述

贪心算法应用:在线租赁问题详解

贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景,下面我将从多个维度全面详细地讲解这个问题及其Java实现。

一、问题定义与理解

1.1 问题描述

在线租赁问题可以描述为:假设你经营一家设备租赁公司,有若干台相同的设备可供出租。客户会在一段时间内陆续提出租赁请求,每个请求包含开始时间和结束时间。你的目标是接受尽可能多的租赁请求,使得这些请求在时间上不冲突。

1.2 问题形式化

给定:

  • 一组租赁请求R = {r₁, r₂, …, rₙ}
  • 每个请求rᵢ = (sᵢ, fᵢ),其中sᵢ是开始时间,fᵢ是结束时间

目标:

  • 选择一个最大子集S ⊆ R,使得对于任何两个请求rᵢ, rⱼ ∈ S,区间[sᵢ, fᵢ)和[sⱼ, fⱼ)不重叠

1.3 实际应用场景

  • 会议室安排
  • 课程表安排
  • 电影院放映厅排片
  • 计算资源分配
  • 医院手术室安排

二、贪心算法策略分析

2.1 可能的贪心策略

对于这类区间调度问题,常见的贪心策略有:

  1. 最早开始时间优先:选择开始时间最早的请求
  2. 最短持续时间优先:选择持续时间(fᵢ - sᵢ)最短的请求
  3. 最少冲突优先:选择与其他请求冲突最少的请求
  4. 最早结束时间优先:选择结束时间最早的请求

2.2 策略正确性分析

经过分析,最早结束时间优先的策略可以产生最优解:

  • 最早开始时间优先:反例 - 一个很早开始但很长的请求可能阻止多个短请求
  • 最短持续时间优先:反例 - 可能存在多个短请求都冲突于一个长请求
  • 最少冲突优先:计算复杂且不一定最优
  • 最早结束时间优先:总是留下最多剩余时间安排其他请求

2.3 贪心选择性质证明

要证明最早结束时间优先策略的正确性:

  1. 设O是最优解,G是我们的贪心解
  2. 设r₁是G中第一个选择的请求,也是最早结束的请求
  3. 如果O的第一个请求不是r₁,我们可以用r₁替换O的第一个请求,仍然保持最优
  4. 通过归纳法,可以证明G与O同样最优

三、算法设计与实现

3.1 算法步骤

  1. 将所有租赁请求按照结束时间升序排序
  2. 初始化选择的请求集合S为空,当前结束时间prev_f为0
  3. 对于每个请求rᵢ按排序后的顺序:
    • 如果sᵢ ≥ prev_f(不冲突):
      • 将rᵢ加入S
      • 更新prev_f = fᵢ
  4. 返回S作为最终选择

3.2 Java实现

import java.util.Arrays;
import java.util.Comparator;
import java.util.ArrayList;
import java.util.List;class RentalRequest {int id;int start;int end;public RentalRequest(int id, int start, int end) {this.id = id;this.start = start;this.end = end;}@Overridepublic String toString() {return "Request " + id + ": [" + start + ", " + end + "]";}
}public class OnlineRentalScheduler {// 贪心算法解决在线租赁问题public static List<RentalRequest> scheduleRentals(RentalRequest[] requests) {// 1. 按照结束时间排序Arrays.sort(requests, new Comparator<RentalRequest>() {@Overridepublic int compare(RentalRequest r1, RentalRequest r2) {return r1.end - r2.end;}});List<RentalRequest> selected = new ArrayList<>();int lastEndTime = 0;// 2. 选择不冲突的请求for (RentalRequest req : requests) {if (req.start >= lastEndTime) {selected.add(req);lastEndTime = req.end;}}return selected;}public static void main(String[] args) {// 示例请求RentalRequest[] requests = {new RentalRequest(1, 1, 4),new RentalRequest(2, 3, 5),new RentalRequest(3, 0, 6),new RentalRequest(4, 5, 7),new RentalRequest(5, 3, 8),new RentalRequest(6, 5, 9),new RentalRequest(7, 6, 10),new RentalRequest(8, 8, 11),new RentalRequest(9, 8, 12),new RentalRequest(10, 2, 13),new RentalRequest(11, 12, 14)};List<RentalRequest> scheduled = scheduleRentals(requests);System.out.println("Selected Rental Requests:");for (RentalRequest req : scheduled) {System.out.println(req);}System.out.println("Total scheduled: " + scheduled.size());}
}

3.3 代码解析

  1. RentalRequest类:表示一个租赁请求,包含ID、开始时间和结束时间
  2. scheduleRentals方法
    • 使用Comparator按结束时间排序
    • 遍历排序后的请求,选择不冲突的请求
  3. main方法:提供测试用例并输出结果

四、算法复杂度分析

4.1 时间复杂度

  • 排序阶段:O(n log n),使用Arrays.sort()的快速排序或归并排序
  • 选择阶段:O(n),只需一次线性扫描
  • 总时间复杂度:O(n log n) 主导

4.2 空间复杂度

  • 排序:O(log n) 的栈空间(Java排序算法的空间复杂度)
  • 存储结果:O(k),k是最终选择的请求数(最坏情况O(n))
  • 总空间复杂度:O(n)

五、算法正确性验证

5.1 示例验证

对于给定的示例请求:

Request 1: [1, 4]
Request 2: [3, 5]
Request 3: [0, 6]
Request 4: [5, 7]
Request 5: [3, 8]
Request 6: [5, 9]
Request 7: [6, 10]
Request 8: [8, 11]
Request 9: [8, 12]
Request 10: [2, 13]
Request 11: [12, 14]

排序后:

Request 3: [0, 6]
Request 1: [1, 4]
Request 2: [3, 5]
Request 4: [5, 7]
Request 5: [3, 8]
Request 6: [5, 9]
Request 7: [6, 10]
Request 8: [8, 11]
Request 9: [8, 12]
Request 10: [2, 13]
Request 11: [12, 14]

贪心选择过程:

  1. 选择Request 1: [1,4], lastEndTime=4
  2. 跳过Request 2: [3,5] (3<4)
  3. 选择Request 4: [5,7], lastEndTime=7
  4. 跳过Request 5-7
  5. 选择Request 8: [8,11], lastEndTime=11
  6. 选择Request 11: [12,14], lastEndTime=14

最终选择4个请求,这是最大可能的不冲突集合。

5.2 边界情况测试

  1. 无请求:返回空列表
  2. 所有请求冲突:只选择第一个结束最早的
  3. 无冲突请求:选择所有请求
  4. 相同结束时间:按排序顺序选择第一个不冲突的

六、算法优化与变种

6.1 多资源情况

当有k个相同的租赁设备时,问题变为k-区间调度问题:

public static List<List<RentalRequest>> scheduleRentalsWithKResources(RentalRequest[] requests, int k) {Arrays.sort(requests, Comparator.comparingInt(r -> r.end));List<List<RentalRequest>> resources = new ArrayList<>();for (int i = 0; i < k; i++) {resources.add(new ArrayList<>());}int[] lastEndTimes = new int[k];for (RentalRequest req : requests) {for (int i = 0; i < k; i++) {if (req.start >= lastEndTimes[i]) {resources.get(i).add(req);lastEndTimes[i] = req.end;break;}}}return resources;
}

6.2 加权区间调度

如果每个请求有不同的权重(利润),贪心算法不再适用,需要使用动态规划:

public static int maxWeightSchedule(RentalRequest[] requests) {Arrays.sort(requests, Comparator.comparingInt(r -> r.end));int n = requests.length;int[] dp = new int[n + 1];for (int i = 1; i <= n; i++) {int profit = requests[i-1].end - requests[i-1].start; // 假设权重为持续时间int prevCompatible = findLastNonConflict(requests, i);dp[i] = Math.max(dp[i-1], (prevCompatible == -1 ? 0 : dp[prevCompatible]) + profit);}return dp[n];
}private static int findLastNonConflict(RentalRequest[] requests, int index) {int low = 0, high = index - 1;while (low <= high) {int mid = (low + high) / 2;if (requests[mid].end <= requests[index-1].start) {if (requests[mid+1].end <= requests[index-1].start) {low = mid + 1;} else {return mid + 1;}} else {high = mid - 1;}}return -1;
}

6.3 在线算法版本

当请求实时到达无法预先排序时,可以使用在线算法:

public class OnlineRentalScheduler {private int lastEndTime = 0;public boolean processRequest(RentalRequest request) {if (request.start >= lastEndTime) {lastEndTime = request.end;return true;}return false;}
}

七、实际应用中的考虑

7.1 请求预处理

在实际应用中,可能需要:

  1. 验证时间有效性(开始<结束)
  2. 处理时间格式转换
  3. 过滤无效请求

7.2 多维度约束

可能需要考虑:

  1. 设备类型匹配
  2. 客户优先级
  3. 价格因素
  4. 地理位置限制

7.3 性能优化

对于大规模数据:

  1. 使用并行排序
  2. 考虑分治策略
  3. 使用更高效的数据结构

八、与其他算法对比

8.1 与动态规划对比

特性贪心算法动态规划
时间复杂度O(n log n)O(n²)或O(n log n)
适用问题具有贪心选择性质的问题具有最优子结构的问题
加权支持不支持支持
实现复杂度简单较复杂

8.2 与回溯算法对比

贪心算法:

  • 效率高
  • 不能保证所有情况的最优解
  • 无法回溯撤销选择

回溯算法:

  • 可以找到所有解
  • 时间复杂度高(O(2^n))
  • 适合小规模问题

九、常见问题与解决方案

9.1 如何处理时间重叠但资源充足的情况?

解决方案:修改冲突检测逻辑,跟踪每个资源的最后使用时间。

9.2 如何扩展算法以支持不同类型的设备?

解决方案:为每种设备类型维护单独的调度列表。

9.3 如何实现实时更新的调度?

解决方案:使用合适的数据结构(如TreeSet)来高效插入和查询。

十、总结

贪心算法在在线租赁问题中提供了高效且简单的解决方案。通过选择最早结束的请求,算法能够最大化可接受的请求数量。Java实现展示了如何通过排序和线性扫描来解决这个问题。虽然贪心算法不能解决所有变种问题(如加权情况),但对于基本的区间调度问题,它是最优的选择。

关键要点:

  1. 贪心算法的核心是做出局部最优选择
  2. 正确性依赖于问题具有贪心选择性质
  3. 排序是此类问题的常见预处理步骤
  4. Java的Comparator接口提供了灵活的排序方式
  5. 算法可以扩展以适应更复杂的实际需求

更多资源:

https://www.kdocs.cn/l/cvk0eoGYucWA

本文发表于【纪元A梦】!


http://www.hkcw.cn/article/phdhnZDZMw.shtml

相关文章

BA-SAM: 用于 Segment Anything 模型的可扩展偏置模式注意力掩码

概要 在本文中&#xff0c;我们解决了 Segment Anything Model &#xff08;SAM&#xff09; 的图像分辨率变化挑战。SAM 以其零样本泛化性而闻名&#xff0c;当面对具有不同图像大小的数据集时&#xff0c;性能会下降。以前的方法倾向于将图像大小调整为固定大小或采用结构修改…

centos8修改IP地址和Hostname

修改ip地址 vim /etc/sysconfig/network-scripts/ifcfg-ens33 BOOTPROTO&#xff1a;设置为 static 表示使用静态 IP 地址。 IPADDR&#xff1a;设置新的 IP 地址。 NETMASK&#xff1a;设置子网掩码。 GATEWAY&#xff1a;设置默认网关&#xff08;可选&#xff0c;但通常需要…

Python Day40 学习(复习学习日志Day5-7)

重新对信贷数据集进行了填补空缺值的操作 自己写的时候&#xff0c;还是出现了问题&#xff1a; 首先是忘记了要定义一下data, 通过data pd.read_csv(data.csv)可以将读取到的数据保存到变量data中&#xff0c;方便后续进行数据分析。 其次&#xff0c;是漏掉了 c data.col…

QML 粒子系统之Affector

目录 基本示例AffectorAge - 改变特定年龄粒子的属性Attractor - 吸引粒子到指定点Friction - 施加摩擦力Gravity - 模拟重力Wander - 随机游走效果Turbulence - 添加湍流效果 下载链接 接上篇QML 粒子系统 (雪花飘落、爆炸粒子效果)&#xff0c;本文继续研究粒子系统中的附属效…

Mac 同时录制系统声音和麦克风声音(OBS + BlackHole 免费版)

&#x1f3ac; 一、你将实现的目标 ✅ 用 OBS 免费录制屏幕时&#xff0c;能同时录到&#xff1a; &#x1f5a5; 系统播放的声音&#xff08;比如视频、PPT音效、背景音乐&#xff09; &#x1f399; 你的麦克风说话声音&#xff08;讲解或旁白&#xff09; &#x1f9f0;…

Pytorch知识点2

Pytorch知识点 1、官方教程2、张量&#x1f9f1; 0、数组概念&#x1f9f1; 1. 创建张量&#x1f4d0; 2. 张量形状与维度&#x1f522; 3. 张量数据类型➗ 4. 张量的数学与逻辑操作&#x1f504; 5. 张量的就地操作&#x1f4e6; 6. 复制张量&#x1f680; 7. 将张量移动到加速…

使用pandas实现合并具有共同列的两个EXCEL表

表1&#xff1a; 表2&#xff1a; 表1和表2&#xff0c;有共同的列“名称”&#xff0c;而且&#xff0c;表1的内容&#xff08;行数&#xff09;<表2的行数。 目的&#xff0c;根据“名称”列的对应内容&#xff0c;将表2列中的“所处行业”填写到表1相应的位置。 实现代…

【农资进销存专用软件】佳易王农资进出库管理系统:赋能农业企业高效数字化管理

一、软件概述与核心优势 &#xff08;一&#xff09;试用版获取方式 资源下载路径&#xff1a;进入博主头像主页第一篇文章末尾&#xff0c;点击卡片按钮&#xff1b;或访问左上角博客主页&#xff0c;通过右侧按钮获取详细资料。 说明&#xff1a;下载文件为压缩包&#xff…

深入理解AMBA总线(七)AHB设计要点和AHB2APB同步桥设计前言

** 深入理解AMBA总线&#xff08;七&#xff09;AHB设计要点和AHB2APB同步桥设计前言 ** 前面的几篇文章介绍了AHB-lite协议。主要内容其实就是文档的介绍加上我自己的一些理解&#xff0c;希望对大家有帮助。今天这篇文章将带来AHB设计需要注意的一些事项&#xff0c;然后带…

消除F/1噪声

目录 简介 如何测量及规定1/f噪声&#xff1f; 1/f噪声对电路有何影响&#xff1f; 如何消除或降低1/f噪声&#xff1f; 简介 本文阐释1/f噪声是什么&#xff0c;以及在精密测量应用中如何降低或消除该噪声。1/f噪声无法被滤除&#xff0c;在精密测量应用中它可能是妨碍实…

洛谷-P3912素数个数题解

P3912 素数个数 题目描述 求 1 , 2 , ⋯ , N 1,2,\cdots,N 1,2,⋯,N 中素数的个数。 输入格式 一行一个整数 N N N。 输出格式 一行一个整数&#xff0c;表示素数的个数。 输入输出样例 #1 输入 #1 10输出 #1 4说明/提示 对于 40 % 40\% 40% 的数据&#xff0c; …

【环境搭建】Java、Python、Nodejs等开发环境搭建

1. 前言 趁着 618 活动&#xff0c;我新换了一台电脑。开发的同学都知道&#xff0c;重新在新电脑搭建开发环境是一件相对繁琐的事&#xff0c;这篇文章我将介绍如何搭建Java&#xff08;jdk、maven等&#xff09;、Python&#xff08;uv、conda等&#xff09;、Nodejs、Docke…

【机器学习基础】机器学习入门核心算法:层次聚类算法(AGNES算法和 DIANA算法)

机器学习入门核心算法&#xff1a;层次聚类算法&#xff08;AGNES算法和 DIANA算法&#xff09; 一、算法逻辑二、算法原理与数学推导1. 距离度量2. 簇间距离计算&#xff08;连接标准&#xff09;3. 算法伪代码&#xff08;凝聚式&#xff09; 三、模型评估1. 内部评估指标2. …

设计模式——迭代器设计模式(行为型)

摘要 本文详细介绍了迭代器设计模式&#xff0c;这是一种行为型设计模式&#xff0c;用于顺序访问集合对象中的元素&#xff0c;同时隐藏集合的内部结构。文章首先定义了迭代器设计模式并阐述了其核心角色&#xff0c;包括迭代器接口、具体迭代器、容器接口和具体容器。接着&a…

【文献阅读】Learning Transferable Visual Models From Natural Language Supervision

摘要 最先进的计算机视觉系统经过训练&#xff0c;可预测一组固定的预先确定的对象类别。这种受限的监督形式限制了它们的通用性和可用性&#xff0c;因为指定任何其他视觉概念都需要额外的标记数据。 直接从关于图像的原始文本中学习是一种很有前途的替代方法&#xff0c;它…

字符函数和字符串函数

目录 1.字符分类函数 2.字符转换函数 3.strlen函数的使用和模拟实现 4.strcpy函数的使用和模拟实现 5.strcat函数的使用和模拟实现 6.strcmp函数的使用和模拟实现 7.strcnpy函数的使用和模拟实现 8.strcnat函数的使用和模拟实现 9.strncmp函数的使用 10.strstr的函数使…

苹果电脑深度清理,让老旧Mac重焕新生

在日常使用苹果电脑的过程中&#xff0c;随着时间推移&#xff0c;系统内会积累大量冗余数据&#xff0c;导致电脑运行速度变慢、磁盘空间紧张。想要让设备恢复流畅&#xff0c;苹果电脑深度清理必不可少。那么&#xff0c;如何进行苹果电脑深度清理呢&#xff1f;接下来为你详…

android binder(1)基本原理

一、IPC 进程间通信&#xff08;IPC&#xff0c;Inter-Process Communication&#xff09;机制&#xff0c;用于解决不同进程间的数据交互问题。 不同进程之间用户地址空间的变量和函数是不能相互访问的&#xff0c;但是不同进程的内核地址空间是相同和共享的&#xff0c;我们可…

2025年ESWA SCI1区TOP,改进成吉思汗鲨鱼算法MGKSO+肝癌疾病预测,深度解析+性能实测

1.摘要 本文针对肝癌&#xff08;HCC&#xff09;早期诊断难题&#xff0c;提出了一种基于改进成吉思汗鲨鱼优化算法&#xff08;MGKSO&#xff09;的计算机辅助诊断系统。由于HCC在早期症状不明显且涉及高维复杂数据&#xff0c;传统机器学习方法易受噪声和冗余特征干扰。为提…

性能测试实例(http和ldap协议压测)

一、某授权服务器生成授权码效率验证&#xff08;http协议&#xff09; 测试背景 在存量数据23万条的情况下&#xff0c;生成一条授权数据&#xff0c;需要10秒左右&#xff0c;用户反应数据生成效率太差&#xff0c;需要优化。初步判断是由于在授权数据生成时&#xff0c;有查…