贪心算法之船舶装载问题
一、问题引入
船舶装载问题是经典的贪心算法应用场景,核心目标是:在给定船舶最大载重量的限制下,计算出最多能装载的物品数量。该问题的关键在于如何选择物品装载顺序,以实现 “数量最多” 的目标,而贪心算法通过 “每次选最优” 的策略高效解决此问题。
二、贪心算法原理
1. 核心思路
贪心算法的核心思想是 “局部最优 → 全局最优”。针对船舶装载问题,“局部最优” 即每次选择当前剩余物品中重量最轻的物品进行装载,因为选择轻的物品能最大限度地节省载重量空间,从而为后续装载更多物品创造条件,最终实现 “装载数量最多” 的全局最优目标。
2. 算法步骤
- 输入物品数量、船舶最大载重量以及每个物品的重量。
- 将所有物品按重量升序排序(关键步骤,为 “选最轻” 提供基础)。
- 从排序后的物品中,依次选取物品并累加重量,直到累加重量超过最大载重量为止。
- 统计并返回已装载的物品数量
三、代码结构与优化分析
1. 原始代码核心逻辑
原始代码通过 main 函数完成输入、排序调用和结果计算,maxLoad 函数实现装载数量统计,但存在可读性差、安全性低等问题,具体表现为:
1. 输入提示与计算逻辑混杂在main函数中,代码冗余。
2. 使用原始数组存储物品重量,存在固定大小限制,灵活性不足。
3. 缺乏输入验证,若用户输入非法值(如负数、非数字),程序可能崩溃。
2. 优化后代码解析
优化后的代码基于 C++ 实现,解决了原始代码的缺陷,结构更清晰、安全性更高,具体代码及核心模块分析如下:
(1)头文件依赖
#include <iostream> // 输入输出流
#include <algorithm> // 包含sort排序函数
#include <vector> // 动态数组,替代原始数组
using namespace std;
(2)核心函数 1:maxLoad(计算最大装载数量)
int maxLoad(const vector<double>& weights, double capacity) {double total = 0.0; // 已装载物品总重量int count = 0; // 已装载物品数量// 遍历排序后的物品(范围for循环,C++11及以上特性)for (const auto& weight : weights) {if (total + weight <= capacity) { // 若能装下当前物品total += weight;++count;} else {break; // 超过载重量,停止装载}}return count;
}
关键优化点:
- 用vector<double>替代原始数组:动态调整大小,避免固定长度限制,提高灵活性。
- const修饰符:确保weights数组在函数内不被修改,提升代码安全性。
- 引用传递(& weights):避免数组拷贝,提高程序运行效率。
(3)核心函数 2:inputWeights(单独处理输入逻辑)
void inputWeights(vector<double>& weights, int n) {cout << "请输入" << n << "个物品的重量: ";for (int i = 0; i < n; ++i) {cin >> weights[i];}
}
关键优化点:
输入逻辑与main函数分离:使main函数更简洁,代码可读性提升,后续维护时可单独修改输入逻辑。
(4)main函数
int main() {int n; // 物品数量double capacity; // 船舶最大载重量cout << "请输入物品数量和船的最大载重量: ";cin >> n >> capacity;vector<double> weights(n); // 动态数组存储物品重量inputWeights(weights, n); // 调用输入函数sort(weights.begin(), weights.end()); // 升序排序(核心步骤)// 输出结果cout << "最多能装载的物品数量: " << maxLoad(weights, capacity) << endl;return 0;
}
执行流程:输入参数→获取物品重量→排序→计算装载数量→输出结果,流程清晰,符合 “贪心算法步骤”。
3. 进一步可优化方向(补充建议)
- 添加输入验证:在inputWeights和main函数中增加判断,如物品数量n需为正整数、物品重量和载重量需为非负数,避免非法输入导致程序异常,示例代码:
// 输入物品数量时验证
while (cin >> n >> capacity, n <= 0 || capacity < 0) {cout << "输入无效!物品数量需为正整数,载重量需非负,请重新输入:";
}// 输入物品重量时验证
while (cin >> weights[i], weights[i] < 0) {cout << "物品重量不能为负,请重新输入:";
}
- 支持浮点数精度控制:若实际场景中对重量精度有要求(如保留 2 位小数),可使用fixed和setprecision控制输出,需添加头文件#include <iomanip>。
四、复杂度分析
1. 时间复杂度
- 排序操作:使用 C++ 标准库 sort 函数,时间复杂度为 O(n log n)。
- 遍历计算:maxLoad 函数中遍历 vector 数组,时间复杂度为 O(n)。
- 整体复杂度:排序是耗时最长的操作,因此整体时间复杂度为 O(n log n),适用于中等规模数据(如 n≤10⁵),效率较高。
2. 空间复杂度
- 主要空间消耗来自存储物品重量的vector数组,空间复杂度为O(n)。
- 无额外复杂数据结构,空间利用率高。
五、核心知识点总结
- 贪心算法在船舶装载问题中的应用逻辑:“选最轻物品 → 最大化装载数量”,核心是排序后依次选择;
- C++ 代码优化技巧:用vector替代原始数组、函数拆分(输入与计算分离)、const修饰符与引用传递;
- 复杂度关键:排序决定时间复杂度,数组存储决定空间复杂度;
- 工程化改进:输入验证、精度控制等细节可提升代码健壮性。