当前位置: 首页 > news >正文

【算法笔记】贪心算法

一、什么是贪心算法?

贪心算法是一种在每一步选择中都采取当前看起来最优(最“贪心”)的策略,从而希望得到全局最优解的算法设计思想。

  • 核心思想:每一步都做出局部最优选择,不回退。
  • 适用场景:问题具有最优子结构且满足贪心选择性质 —— 即局部最优可以导出全局最优。

二、贪心算法的典型流程

  1. 排序/预处理:对待选元素进行必要的排序或组织。
  2. 局部选择:按照某种规则(如最大收益、最小代价等)依次选取元素。
  3. 可行性检验:检查当前选择是否满足约束。
  4. 解的构造:在每次选择的基础上逐步构建最终解。

三、经典例题回顾

1. 活动选择问题

  • 题目:有 n n n 个活动,每个活动有开始时间 s i s_i si 和结束时间 f i f_i fi,要求选出最多互不冲突的活动集合。
  • 贪心策略:按活动结束时间从小到大排序,每次选取结束最早且与当前已选活动不冲突的活动。

2. 分数背包问题(Fractional Knapsack)

  • 题目:有 n n n 件物品,每件物品重量 w i w_i wi,价值 v i v_i vi,背包容量 W W W。物品可分割装入。
  • 贪心策略:按单位重量价值 v i w i \frac{v_i}{w_i} wivi 从大到小装入;装不下时装入尽可能多的部分。

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

  • 题目:给定带权无向图,求一棵权值之和最小的生成树。
  • 贪心策略:对所有边按权值从小到大排序,依次加入不会形成环的最小边。

四、实战题目 —— 给 n n n 个国家加税

4.1 题目描述

  • n n n 个国家,初始关税税率均为 100%。
  • 对第 i i i 个国家,加税一次可将其税率提升 p i % p_i\% pi%(即税率从上一次的值再加上 p i p_i pi 百分点)。
  • 允许一共进行 k k k 次加税操作,每次只能选择一个国家进行一次加税。
  • 求经过 k k k 次加税后,所有国家税率的累乘(乘积)的最大值。

示例

输入:n = 3, p = [2, 5, 3], k = 4  
输出:最大乘积(按百分比计算)

4.2 贪心思路分析

收益定义
  • 对第 i i i 个国家当前税率 t i t_i ti(最开始 t i = 100 % t_i=100\% ti=100%)再加一次 p i % p_i\% pi%,其新的税率为 t i + p i t_i + p_i ti+pi
  • 在乘积中,相当于将当前乘积乘以 t i + p i t i \frac{t_i + p_i}{t_i} titi+pi,因此这次操作对总乘积的放大倍数为:

r i = t i + p i t i = 1 + p i t i r_i = \frac{t_i + p_i}{t_i} = 1 + \frac{p_i}{t_i} ri=titi+pi=1+tipi

  • 要使乘积最大,每次都应选择能带来最大放大倍数 r i r_i ri 的国家。
优先队列实现
  • 使用一个最大堆(priority_queue)存储每个国家当前可获得的放大倍数 r i r_i ri
  • 每次取出堆顶( r i r_i ri 最大的国家),实施一次加税:
    1. 更新该国家税率: t i ← t i + p i t_i \leftarrow t_i + p_i titi+pi
    2. 计算新的放大倍数: r i ← 1 + p i t i r_i \leftarrow 1 + \frac{p_i}{t_i} ri1+tipi
    3. 将更新后的 r i r_i ri 重新压入堆中。
  • 重复上述过程 k k k 次,结束后遍历所有国家税率,计算乘积。

4.3 代码示例(C++)

#include <bits/stdc++.h>
using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int n, k;cin >> n >> k;vector<double> p(n), t(n, 100.0);for (int i = 0; i < n; i++) {cin >> p[i];}// 优先队列:pair<放大倍数, 国家索引>auto cmp = [](const pair<double,int>& a, const pair<double,int>& b) {return a.first < b.first;};priority_queue<pair<double,int>, vector<pair<double,int>>, decltype(cmp)> pq(cmp);// 初始化for (int i = 0; i < n; i++) {double r = 1.0 + p[i] / t[i];pq.push({r, i});}// 执行 k 次加税while (k--) {auto [r, i] = pq.top(); pq.pop();t[i] += p[i];r = 1.0 + p[i] / t[i];pq.push({r, i});}// 计算累乘结果double ans = 1.0;for (double tax : t) {ans *= tax / 100.0;}cout << fixed << setprecision(6) << ans << "\n";return 0;
}

4.4 复杂度分析

  • 每次操作:弹出堆顶 + 插入堆顶,各 O ( log ⁡ n ) O(\log n) O(logn)
  • 总共 k k k 次操作,时间复杂度为: O ( k log ⁡ n ) O(k \log n) O(klogn)
  • 空间复杂度:需要存储 n n n 个国家的信息,为 O ( n ) O(n) O(n)

五、更多贪心练习题推荐

  1. 区间染色问题:给定区间集合,最少使用多少种颜色使重叠区间不同色?
  2. 跳跃游戏 II:每格有最大跳跃长度,求最少跳跃次数到达末尾。
  3. 分配饼干:孩子有满足度,饼干有大小,如何让最多孩子满足?
  4. 连通区间合并:给一堆区间,合并所有重叠区间,输出不重叠区间集合。

六、小结

  • 贪心算法以“当前最优选择”逐步构建解,适合于“最优子结构”且满足“贪心选择性质”的问题。
  • 真正的难点在于如何证明局部最优能导出全局最优,以及如何设计合适的贪心策略
  • 通过上述“加税”题,以及经典例题的练习,可以加深对贪心算法的理解与应用。
http://www.xdnf.cn/news/183673.html

相关文章:

  • Charles 抓包入门教程
  • 代码随想录算法训练营第60期第二十天打卡
  • 详细图解 Path-SAM2: Transfer SAM2 for digital pathology semantic segmentation
  • git每次push都要输入用户名和密码很繁琐,只在第一次输入之后都不需要的解决方法
  • 使用PHP对接印度股票市场数据
  • 睿享会丨走进西安御品轩
  • 代码随想录第28天:动态规划1
  • 每日c/c++题 备战蓝桥杯(P2392 kkksc03考前临时抱佛脚)
  • 若依/RuoYi 内置功能
  • tensor 的连续性 与 contiguous() 方法
  • 全星APQP软件系统:驱动芯片半导体行业研发管理迈向高效与合规新高度
  • 远程通信历史上为什么电话网络从模拟信号转向了数字信号?
  • Super Sample Tasker 学习-1
  • disruptor-spring-boot-start版本优化升级
  • LeetCode 每日一题 2025/4/21-2025/4/27
  • C++初阶-模板初阶
  • 杭电oj(1008、1012、1013、1014、1017)题解
  • 【文心快码】确实有点东西!
  • Redis 通用命令与keyspace
  • element-ui dropdown 组件源码分享
  • QML中的色彩应用
  • 调度算法的模拟及应用
  • 接口测试详解
  • electron-vite 应用打包自定义图标不显示问题
  • 28-29【动手学深度学习】批量归一化 + ResNet
  • Java线程池详解
  • 2024年12月GESP 图形化 一级考级真题——飞行的小猫
  • Linux的例行性工作(crontab)
  • 码蹄杯——tips
  • MAGI-1: Autoregressive Video Generation at Scale