贪心算法(最优装载问题)

贪心算法

贪心算法(Greedy Algorithm)是一种在每一步决策中都采取当前状态下最优选择 的启发式策略,其核心思想是通过**"局部最优"逐步逼近"全局最优"。**它不追求对所有可能情况的枚举,而是基于某种贪心策略快速做出选择,最终希望等到问题的最优解。

核心思想

贪心算法的本质是"视短"的,它只关注当前步骤中最好的选择(局部最优),不考虑该选择对未来步骤的影响,也不回溯修改之前决策。其逻辑可概括为:

  1. 对问题进行分解,将其拆分为一系列可独立决策的子问题;
  2. 定义一个 "贪心策略"(如 "选最小""选最早""选单位价值最高" 等),用于在每个子问题中选择局部最优解;
  3. 逐步执行这些局部最优选择,累积结果,最终形成全局解。

使用条件

并非所有问题都能用贪心算法求解,它仅适用于满足以下两个条件的问题:

  1. 贪心选择性质

    全局最优解可以通过一系列局部最优选择(贪心选择)得到。即,存在一种策略,使得每次选择的局部最优解能最终组合成全局最优解。

  2. 最优子结构

    问题的全局最优解包含其子问题的最优解。即,若将问题拆分为子问题,子问题的最优解可用于构建原问题的最优解。

基本步骤

  1. 问题建模:将问题转化为 "多步选择" 的形式,明确每个步骤的可选方案和目标(如 "最大化收益""最小化成本")。

  2. 确定贪心策略:设计一个规则,用于在每一步从可选方案中选择 "局部最优" 的选项(如 "选结束时间最早的活动""选单位重量价值最高的物品")。

  3. 证明策略有效性:验证该贪心策略是否满足 "贪心选择性质" 和 "最优子结构"(关键步骤,否则可能得到非最优解)。

  4. 逐步求解:按贪心策略依次选择,累积结果,得到最终解。

典型应用场景

贪心算法在许多经典问题中被证明有效,例如:

1. 活动选择问题

问题 :有若干活动,每个活动有开始时间和结束时间,选择最多的不重叠活动。
贪心策略 :每次选择结束时间最早 的活动,然后排除与其冲突的活动,重复此过程。
原理:结束时间最早的活动能留下更多时间给后续活动,最终可得到最多不重叠活动。

2. 哈夫曼编码(Huffman Coding)

问题 :为字符设计前缀码(无歧义编码),使总编码长度最短(压缩最优)。
贪心策略 :每次选择频率最低的两个字符 合并为一个新节点,重复此过程构建哈夫曼树,树的路径即为编码。
原理:频率低的字符用较长编码,频率高的用较短编码,总长度最优。

3. 最小生成树(Prim/Kruskal 算法)

问题:在连通图中选择 n-1 条边,使所有节点连通且总权重最小。

贪心策略

Prim 算法:从某节点出发,每次选择连接树内外的最小权重边

Kruskal 算法:按边权重从小到大排序,每次选择不形成环的边

原理:通过局部选择最小权重边,最终形成全局权重最小的生成树。

4. 单源最短路径(Dijkstra 算法)

问题 :从起点到其他所有节点的最短路径(边权重非负)。
贪心策略 :每次选择当前已知最短路径的节点 ,并用其更新相邻节点的路径长度。
原理:由于边权重非负,一旦某节点被选中,其最短路径即确定,无需回溯。

贪心算法的框架

例子:最优装载问题

要求:物品不可分割,要求装在最多货物。

思路:

(1)贪心策略:在所有货船里面每次选择重量最小的。

(2)局部最优解:根据贪心策略,一步一步地得到局部最优解,

例如:第一次选择一个重量最小的古董,记位置a1,第二次再从剩下的再选最小的记为a2,一次类推....

(3) 把所有的局部最优解合为原来问题的一个最优解(a1、a2......)

(4)设计古董重量的排序。(冒泡排序,快速排序都行)如图:

代码:

cpp 复制代码
#include<stdio.h>

// 函数功能:用贪心算法选择最多数量的古董(优先选轻的)
// 参数:N-古董总数,W-最大载重,q-古董重量数组
int loadAntiques(int N, double W, double q[])
{
	int ans = 0;        // 已装载的古董数量
	double totalWeight = 0.0;  // 已装载的总重量
	
	// 1. 冒泡排序:将古董按重量从小到大排序(贪心策略基础)
	for (int i = 0; i < N - 1; i++)
	{
		for (int j = 0; j < N - i - 1; j++)
		{
			// 若前一个比后一个重,则交换(注意用double类型临时变量)
			if (q[j] > q[j + 1])
			{
				double temp = q[j];  // 修正:temp应为double类型(原代码用int错误)
				q[j] = q[j + 1];
				q[j + 1] = temp;
			}
		}
	}
	
	// 2. 贪心选择:从最轻的开始装,直到超重
	for (int i = 0; i < N; i++)
	{
		// 修正:先判断加上当前古董是否超重,不超重才装载(原代码逻辑反了)
		if (totalWeight + q[i] <= W)
		{
			totalWeight += q[i];
			ans++;
		}
		else
		{
			break;  // 超重则停止装载
		}
	}
	
	// 输出结果
	printf("最大古董数量为:%d个\n", ans);
	printf("船上总重量为:%lf吨\n", totalWeight);
	
	return 0;
}

int main()
{
	int N = 8;          // 古董总数
	double W = 30.0;    // 船的最大载重
	double q[8];        // 存储每个古董的重量
	
	// 修正:循环读取8个古董的重量(原代码只读取了1个)
	printf("请输入8个古董的重量(吨):\n");
	for (int i = 0; i < N; i++)
	{
		scanf("%lf", &q[i]);  // 注意取地址符&
	}
	
	// 调用函数计算最大装载量
	loadAntiques(N, W, q);
	
	return 0;
}

结果:

相关推荐
CoovallyAIHub3 小时前
CVPR 2025 | 频率动态卷积(FDConv):以固定参数预算实现频率域自适应,显著提升视觉任务性能
深度学习·算法·计算机视觉
mit6.8243 小时前
[rStar] 解决方案节点 | `BaseNode` | `MCTSNode`
人工智能·python·算法
晴空闲雲4 小时前
数据结构与算法-树和二叉树-二叉树的存储结构(Binary Tree)
数据结构·算法
索迪迈科技5 小时前
Flink Task线程处理模型:Mailbox
java·大数据·开发语言·数据结构·算法·flink
元亓亓亓6 小时前
LeetCode热题100--230. 二叉搜索树中第 K 小的元素--中等
算法·leetcode·职场和发展
草莓熊Lotso6 小时前
《算法闯关指南:优选算法-双指针》--01移动零,02复写零
c语言·c++·经验分享·算法·leetcode
焜昱错眩..7 小时前
代码随想录算法训练营第三十九天|62.不同路径 63.不同路径ll
算法
焦耳加热11 小时前
阿德莱德大学Nat. Commun.:盐模板策略实现废弃塑料到单原子催化剂的高值转化,推动环境与能源催化应用
人工智能·算法·机器学习·能源·材料工程
wan5555cn11 小时前
多张图片生成视频模型技术深度解析
人工智能·笔记·深度学习·算法·音视频