算法学习日记 |贪心算法

🧠 算法学习日记 | 今天我用「贪心算法」解了四道题,原来"局部最优"也能通向全局最优!

大家好,我是你们的算法学习搭子 👋

今天继续我的算法入门之旅,重点练习了**贪心算法(Greedy Algorithm)**这一经典而强大的思想。

很多人觉得"贪心"就是"随便选",但其实,它是一种基于直觉+数学证明 的策略。

我们通过选择当前看起来最好的选项,来期望最终得到全局最优解。

今天我完整做了四道题,每一道都坚持用最朴素的贪心逻辑实现。下面我把题目原文我的原始代码原封不动贴出来,不做任何删减或美化,只为真实记录学习过程。


🔹 题目一:最小化战斗力差距

问题描述

小蓝是机甲战队的队长,他手下共有 n 名队员,每名队员都有一个战斗力值 w_i 。现在他需要将这 n 名队友分成两组 a b ,分组必须满足以下条件:

  • 每个队友都属于 a 组或 b 组。
  • a 组和 b 组都不为空。
  • 战斗力差距最小。

战斗力差距的计算公式为 \|\\max(a) - \\min(b)\| ,其中 \\max(a) 表示 a 组中战斗力最大的, \\min(b) 表示 b 组中战斗力最小的。

请你计算出可以得到的最小战斗力差距。

输入格式

第一行一个整数 n ,表示队员个数。

第二行 n 个整数 w_1, w_2, \\ldots, w_n ,分别表示每名队友的战斗力值。

数据范围保证: 2 \\leq n \\leq 10\^5 ,,, 1 \\leq w_i \\leq 10\^9

输出格式

输出一个整数,表示可以得到的最小战斗力差距。
样例输入

复制代码
3
1 2 3

样例输出

复制代码
1

说明

样例中,当 a = \[1,3\] ,,, b = \[2\] ,此时战斗力差距为 1,无法得到比 1 更小的安排方式。

✅ 我的代码(完全保留原始写法)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
int a[N];

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    sort(a+1,a+1+n);
    int ans = a[2] - a[1];
    for(int i=1;i<n;++i)ans = min(ans,a[i+1]-a[i]);
    cout<<ans;
    return 0;
}

🔹 题目二:谈判

题目描述

在很久很久以前,有 n 个部落居住在平原上,依次编号为 1 到 n 。第 i 个部落的人数为 t_i

有一年发生了灾荒。年轻的政治理家小蓝想要说服所有部落一同应对灾荒,他能通过谈判来说服部落进行联合。

每次谈判,小蓝只能邀请两个部落参加,花费的金币数量为两个部落的人数之和,谈判的效果是两个部落联合成一个部落(人数为原来两个部落的人数之和)。

输入描述

输入的第一行包含一个整数 n ,表示部落的数量。

第二行包含 n 个正整数,依次表示每个部落的人数。

其中, 1 \\leq n \\leq 1000 ,,, 1 \\leq t_i \\leq 10\^4

输出描述

输出一个整数,表示最小花费。
输入输出样例

复制代码
输入
4
9 1 3 5

输出
31

✅ 我的代码(完全保留原始写法)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e4+5;
long int a[N];

int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    sort(a+1,a+1+n);
    int ans = 0;
    for(int i=1;i<n;++i){
        a[i+1] = a[i+1] + a[i];
        ans += a[i+1];
    }
    cout<<ans;
    return 0;
}

🔹 题目三:纪念品分组

问题描述

元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参加晚会的同学所获得的纪念品价值相对均衡,他要把购来的纪念品根据价格进行分组,但每组最多只能包括两件纪念品,并且每组纪念品的价格之和不能超过一个给定的整数 w 。为了保证在尽量短的时间内发完所有纪念品,乐乐希望分组的数目最少。

你的任务是写一个程序,找出所有分组方案中分组数最少的一种,输出最少的分组数目。

输入描述

第 1 行包括一个整数 w ((( 80 \\leq w \\leq 200 ),为每组纪念品价格之和的上限。

第 2 行为一个整数 n ((( 1 \\leq n \\leq 30000 ),表示购来的纪念品的总件数。

第 3 ~ n+2 行每行包含一个正整数 p_i ((( 5 \\leq p_i \\leq w ),表示所对应纪念品的价格。
输出描述

输出一行,包含一个整数,即最少的分组数目。
输入输出样例

复制代码
输入
166
9
90
26
20
30
50
60
70
80
90

输出
6

✅ 我的代码(完全保留原始写法)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 3e4 + 5;
int a[N];

int main() {
    int w, n;
    cin >> w >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    sort(a + 1, a + 1 + n);

    int l = 1, r = n, ans = 0;
    while (l <= r) {
        if (a[l] + a[r] <= w) {
            l++;
        }
        r--;
        ans++;
    }
    cout << ans << endl;
    return 0;
}

🔹 题目四:分糖果

问题描述

最近暑期特训算法班的同学们表现出色,他们的老师肖恩决定给他们分发糖果。肖恩购买了 n 个不同种类的糖果,用小写的阿拉伯字母表示。每个糖果必须分发给一个同学,并且每个同学至少要分到一个糖果。同学们的开心程度定义为他们所分到的糖果组成的字符串 s\[i\] 的字典序。肖恩希望同学们的开心程度相差尽量小,因此他要找到一种方案,使得所有糖果组成的字符串中字典序最大的字符串尽可能小。请输出能够实现字典序最小可能的 \\max(s\[1\], s\[2\], s\[3\], \\ldots, s\[x\])
输入描述

第一行输入两个整数 n x ,分别表示有 n 个糖果 x 个同学。

第二行输入一个长度为 n 的字符串 S ,,, S\[i\] 表示第 i 个糖果的种类。

数据保证 1 \\leq n \\leq 10\^6 ,,, 1 \\leq x \\leq n ,,, S\[i\] \\in \['a','z'\]

输出描述

输出一个字符串,为所有糖果组成的字符串中字典序最大的字符串最小的可能值。
样例输入

复制代码
6 2
caabdc

样例输出

复制代码
abcdd

说明

一个最优分配方案是一个同学拿到 abccd,一个同学拿到 a

✅ 我的代码(完全保留原始写法)

cpp 复制代码
#include<bits/stdc++.h>
const int N=1e6+9;
using namespace std;
char s[N];

int main(){
    int n,x;
    cin>>n>>x;
    cin>>s+1;
    sort(s+1,s+1+n);
    if(s[1]==s[n]){
        for(int i=1;i<=n/x+(n%x?1:0);i++) cout<<s[i];
    }
    else if(s[1]==s[x]){
        for(int i=x;i<=n;i++) cout<<s[i];
    }
    else cout<<s[x];
    return 0;
}

🌟 我的思考

这四道题虽然形式各异,但都用了贪心的核心思想

  • 最小化战斗力差距:排序后相邻元素差最小 → 贪心取最小差值
  • 谈判:每次合并最小的两个部落 → 类似哈夫曼编码
  • 纪念品分组:双指针从两端匹配 → 尽量让大数和小数配对
  • 分糖果:排序后按字典序最小分配 → 最优解是尽可能让最大串变小

你会发现:

贪心的本质是"局部最优 → 全局最优" ,但它不一定总是正确,必须有数学证明支持。

比如:

  • "谈判"题其实是哈夫曼树的经典应用,每次合并代价最小的两个节点
  • "纪念品分组"是双指针+贪心,先排序,再从两边夹逼
  • "分糖果"是排序+分类讨论,目标是最小化最大字典序

✅ 总结

  • 贪心算法适用于:具有最优子结构的问题
  • 常见技巧:排序、优先队列、双指针、区间覆盖
  • 关键在于:证明贪心选择性质(即当前最优不会影响后续最优)
  • 不是所有问题都能用贪心解决,需谨慎使用
  • 但一旦适用,效率极高,代码简洁

相关推荐
知识分享小能手1 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019 规则、默认值和完整性约束 —— 语法详解与实战案例(11)
sql·学习·sqlserver
清钟沁桐1 小时前
算法实现
算法
梦游钓鱼2 小时前
c++中一维数组和二维数组的应用
数据结构·c++·算法
铁甲前沿2 小时前
基于最小二乘拟合减小四轮定位数据采集误差的方法
算法
此刻觐神2 小时前
Windows学习笔记-18(MFC项目-制作快捷方式管理工具)
windows·笔记·学习·mfc
郝学胜-神的一滴2 小时前
深入解析Effective Modern C++条款35:基于任务与基于线程编程的哲学与实践
开发语言·数据结构·c++·程序人生
汉克老师2 小时前
GESP2024年9月认证C++二级( 第一部分选择题(9-15))
c++·循环结构·分支结构·gesp二级·gesp2级·求余数
程序员酥皮蛋2 小时前
hot 100 第二十六题 26.环形链表 II
算法
大鹏的NLP博客2 小时前
HuggingFace WordPiece Tokenizer in C++
c++·hf tokenizer