#6548. 2026/4/18/XRC笔记(简单贪心)
2026/4/18/XRC笔记(简单贪心)
简单贪心笔记
题1:P2027 当总统
题型定位
入门贪心 | 最小代价优先 | 排序基础应用题
核心考点
- 贪心核心策略:最小代价优先,用最少成本达成目标
- 升序排序的基础应用
- 整数除法的边界处理、多组测试用例的变量初始化
题目大意
小明竞选总统,需要获得超过一半的州的支持才能当选;每个州中,需要获得超过一半的选民支持,才能赢得该州。给出每个州的选民人数,求小明当选总统至少需要赢得多少选民的支持。
贪心核心思路
- 要最小化总选民数,必须优先拿下选民最少的州(拿下每个州的成本更低);
- 要超过半数的州,最少需要拿下
m/2 + 1个州(m为州总数,奇偶均适用); - 对每个州,拿下它所需的最少选民数为
该州选民数/2 + 1(超过半数,整数除法向下取整); - 对所有州的选民数升序排序,取前
m/2 + 1个州,累加每个州的最小成本即可。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long // 防数据溢出,统一用long long
int n,m,a[110]; // n组测试用例,m为州数,a存储每个州的选民数
signed main(){
cin>>n;
while(n--){ // 处理多组测试用例
cin>>m;
for(int i=1;i<=m;i++){
cin>>a[i];
}
int ans=0;
sort(a+1,a+1+m); // 升序排序,优先选选民最少的州
// 取超过半数的州:m/2+1个,保证当选
for(int i=1;i<=m/2+1;i++){
ans+=a[i]/2+1; // 累加拿下该州的最小选民数
}
cout<<ans<<"\n";
}
return 0;
}
关键代码解析
sort(a+1,a+1+m):升序排序是贪心的核心,保证我们能以最低成本拿到足够的州;m/2 + 1:无论m是奇数还是偶数,都能保证州的数量超过半数。例:m=3,取2个;m=6,取4个;a[i]/2 + 1:整数除法自动向下取整,完美实现“超过半数”的要求,例:5个选民,5/2+1=3,刚好超过半数。
易错点&复习提醒
- 多组测试用例中,
ans必须在每组循环内初始化,否则会累加前一组的结果,导致答案错误; - 数组下标注意:代码从1开始存储,sort的结束位置是
a+1+m,不要写成a+m导致最后一个元素未排序; - 数据范围:本题m≤101,选民数≤100,无需担心溢出,但养成用
long long的习惯,避免后续大数据题踩坑。
题2:P2521 纪念品分组
题型定位
经典贪心 | 双指针贪心 | 最优装载问题模板题
核心考点
- 贪心核心策略:最大+最小配对优先,最大化每组利用率
- 排序+双指针算法
- 最少分组问题的经典模型
题目大意
有n件纪念品,每件有一个价格,每组最多放2件纪念品,且每组的总价不能超过上限w。求最少需要分成多少组,才能装完所有纪念品。
贪心核心思路
- 要让分组数最少,就要尽可能让每组都装2件,减少单独成组的情况;
- 对所有纪念品价格升序排序,左指针
l指向当前最便宜的纪念品,右指针r指向当前最贵的纪念品; - 若最便宜+最贵的总价≤w:两人同组,
l右移,r左移,组数+1; - 若总价超过w:最贵的纪念品无法和任何其他纪念品同组,只能单独成组,
r左移,组数+1; - 循环直到
l>r,所有纪念品处理完毕。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[300010],w,ans; // w为每组总价上限,ans为分组数
signed main(){
cin>>w>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n); // 升序排序,为双指针配对做准备
int l=1,r=n; // 左指针:当前最便宜,右指针:当前最贵
while(l<=r){
if(a[l]+a[r]<=w){ // 可以配对,同组
ans++;
l++;
r--;
}else{ // 无法配对,最贵的单独成组
ans++;
r--;
}
}
cout<<ans;
return 0;
}
关键代码解析
- 排序是双指针贪心的前提:只有有序数组,才能保证“最大+最小”的配对策略能得到全局最优解;
- 循环条件
l<=r:当l==r时,只剩最后一件纪念品,会进入else分支,单独成组,不会遗漏; - 时间复杂度:排序O(nlogn),双指针遍历O(n),整体O(nlogn),能轻松处理n≤3e5的大数据范围。
易错点&复习提醒
- 循环边界必须是
l<=r,不能写成l<r,否则会漏掉最后一件纪念品,导致答案少1; - 本题严格限制每组最多2件,此贪心策略仅适用于该场景,不可直接套用到每组可装多件的场景;
- 数组大小要开够,本题n最大3e5,必须开3e5以上的数组,避免数组越界。
题3:T1262 [GESP202312 四级T2] 田忌赛马
题型定位
经典贪心 | 最优胜负配对 | 排序匹配模板题
核心考点
- 贪心核心策略:用最小的代价赢下对方,最大化获胜场次
- 双数组排序+贪心匹配
- GESP/CSP-J高频考点
题目大意
你和田忌各有n匹马,每匹马有唯一的速度,速度快的马会赢得比赛。你可以自由安排自己马的出场顺序,求最多能赢田忌多少场比赛。
贪心核心思路
- 要最大化获胜场次,就要用刚好能赢对方的最慢的马去赢,避免用快马赢对方的慢马,浪费战力;
- 将你的马(a数组)和田忌的马(b数组)都升序排序;
- 用双指针匹配:遍历你的马,若当前你的马能赢田忌当前最慢的未匹配的马,就赢下这一场,田忌的指针后移,获胜场次+1;
- 若赢不了,就用这匹马去消耗田忌更快的马,不浪费赢的机会。
AC代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,a[50010],b[50010],sb=1,ans; // sb是田忌马的指针,ans是获胜场次
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]; // 你的马的速度
}
for(int i=1;i<=n;i++){
cin>>b[i]; // 田忌的马的速度
}
sort(a+1,a+1+n); // 你的马升序排序
sort(b+1,b+1+n); // 田忌的马升序排序
for(int i=1;i<=n;i++){
// 用当前最小的能赢的马,赢田忌当前最慢的马
if(a[i]>b[sb]){
sb++;
ans++;
}
}
cout<<ans;
return 0;
}
关键代码解析
- 双升序排序:保证我们能从小到大依次匹配,用最小的代价赢下比赛,实现赢场最大化;
- 核心匹配逻辑
a[i]>b[sb]:只有当你的马能赢田忌当前未匹配的最慢的马时,才选择赢下,避免浪费快马; - 时间复杂度:排序O(nlogn),遍历O(n),整体O(nlogn),适配n≤5e4的范围。
易错点&复习提醒
- 本代码是基础核心版,仅实现“最大化赢场”的最简贪心,完整的田忌赛马还需要处理平局、用最慢的马消耗对方最快的马的场景,进阶复习时需掌握完整版;
- 两个数组必须都排序,且排序方向一致,否则贪心策略失效;
- 指针
sb的初始值必须和数组起始下标一致(本题从1开始),避免匹配错位。
题4:P2526 蜡烛
题型定位
入门贪心 | 模拟枚举贪心 | 排序应用题
核心考点
- 贪心核心策略:降序排序优先用最长的蜡烛,保证坚持最久
- 降序排序+模拟枚举
- 边界条件判断
题目大意
有n根蜡烛,每根蜡烛的长度为a[i],每根蜡烛每燃烧1小时会减少1单位长度。第1天需要点燃1根蜡烛,第2天需要点燃2根蜡烛,……,第k天需要点燃k根蜡烛,每天点燃的蜡烛必须持续燃烧1小时。请问你最多能连续坚持多少天?
贪心核心思路
- 要坚持最久,每天都要优先用当前最长的蜡烛,避免短蜡烛提前用完,导致后续无法凑够数量;
- 枚举天数k(代码中为ni),从1开始依次尝试;
- 每次枚举前,将蜡烛长度降序排序,检查前k根蜡烛是否都能燃烧1小时(长度>0);
- 若可以,将前k根蜡烛各减1,继续尝试下一天;若不行,说明最多能坚持k-1天,结束枚举。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,a[60];
// 降序排序比较函数,保证每次排序后,蜡烛长度从长到短排列
bool cmp(int s1,int s2){
return s1>s2;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i]; // 每根蜡烛的长度
}
int day=0; // 枚举当前尝试的天数
while(day<=n){ // 最多坚持n天(第n天需要n根蜡烛,超过n根不可能)
day++;
sort(a+1,a+1+n,cmp); // 降序排序,优先用最长的蜡烛
bool ok=1; // 标记当天是否能坚持
// 检查前day根蜡烛是否都能燃烧1小时
for(int i=1;i<=day;i++){
if(a[i]==0){
ok=0;
break;
}
}
if(!ok)break; // 无法坚持,退出循环
// 当天可以坚持,前day根蜡烛各燃烧1小时
for(int i=1;i<=day;i++)a[i]--;
}
cout<<day-1; // 最多能坚持的天数
return 0;
}
关键代码解析
- 降序排序:每次枚举天数前都降序排序,保证我们优先使用最长的蜡烛,最大化蜡烛的利用率,是贪心策略的核心;
- 枚举边界
day<=n:第k天需要k根蜡烛,而总共有n根蜡烛,所以最多只能坚持n天,避免无效枚举; - 核心判断:只有前day根蜡烛都>0,才能满足当天的需求,否则无法坚持。
易错点&复习提醒
- 排序的比较函数
cmp必须是降序,若写成升序,会优先用短蜡烛,导致答案偏小,直接错误; - 最终输出的是
day-1,因为循环中day先自增,再判断是否能坚持,无法坚持时,最后一次有效的天数是day-1; - 本代码是暴力模拟版,适合n≤60的小数据范围,进阶复习时需掌握O(nlogn)的二分优化版,适配更大的数据范围。
4道贪心题 核心考点汇总表
| 洛谷题号 | 题目名称 | 核心贪心策略 | 排序方向 | 核心算法结构 | 难度 |
|---|---|---|---|---|---|
| P2027 | 当总统 | 最小代价优先,用最低成本达成目标 | 升序 | 排序+前缀累加 | 3 |
| P2521 | 纪念品分组 | 最大+最小配对,最大化每组利用率 | 排序+双指针 | ||
| T1262 | 田忌赛马 | 最小代价赢对方,最大化获胜场次 | 双升序 | 排序+双指针匹配 | 2 |
| P2526 | 蜡烛 | 降序优先用最长的,保证坚持最久 | 降序 | 排序+枚举模拟 | 3 |
复习核心提醒
- 贪心算法的核心是局部最优推导出全局最优,每道题的排序都是为了给贪心策略提供基础,必须先理解“为什么这个贪心策略是对的”,再背诵代码;
- 所有模板题的三大易错点:数组下标、循环边界、多组用例的变量初始化,这是考试中最容易丢分的地方,复盘时重点检查;
- 入门阶段先掌握模板代码,再逐步拓展优化版和变体题型,循序渐进提升贪心思维。