csp信奥赛C++高频考点专项训练之贪心算法 --【区间贪心】:种树

题目背景
一条街的一边有几座房子,因为环保原因居民想要在路边种些树。
题目描述
路边的地区被分割成块,并被编号成 1 , 2 , ... , n 1, 2, \ldots,n 1,2,...,n。每个部分为一个单位尺寸大小并最多可种一棵树。
每个居民都想在门前种些树,并指定了三个号码 b b b, e e e, t t t。这三个数表示该居民想在地区 b b b 和 e e e 之间(包括 b b b 和 e e e)种至少 t t t 棵树。
居民们想种树的各自区域可以交叉。你的任务是求出能满足所有要求的最少的树的数量。
输入格式
输入的第一行是一个整数,代表区域的个数 n n n。
输入的第二行是一个整数,代表房子个数 h h h。
第 3 3 3 到第 ( h + 2 ) (h + 2) (h+2) 行,每行三个整数,第 ( i + 2 ) (i + 2) (i+2) 行的整数依次为 b i , e i , t i b_i, e_i, t_i bi,ei,ti,代表第 i i i 个居民想在 b i b_i bi 和 e i e_i ei 之间种至少 t i t_i ti 棵树。
输出格式
输出一行一个整数,代表最少的树木个数。
输入输出样例 1
输入 1
9
4
1 4 2
4 6 2
8 9 2
3 5 2
输出 1
5
数据规模与约定
对于 100 % 100\% 100% 的数据,保证:
- 1 ≤ n ≤ 3 × 10 4 1 \leq n \leq 3 \times 10^4 1≤n≤3×104, 1 ≤ h ≤ 5 × 10 3 1 \leq h \leq 5 \times 10^3 1≤h≤5×103。
- 1 ≤ b i ≤ e i ≤ n 1 \leq b_i \leq e_i \leq n 1≤bi≤ei≤n, 1 ≤ t i ≤ e i − b i + 1 1 \leq t_i \leq e_i - b_i + 1 1≤ti≤ei−bi+1。
思路分析
本题要求满足所有区间"至少种 t 棵树"的最少总植树数。核心贪心策略:
将区间按右端点从小到大排序,对于每个区间,先统计已种树数量,若不足则优先在区间右端空闲位置补种。这样每棵树尽可能被后续区间覆盖,从而减少总数。
采用朴素标记数组实现:
- 读取
n(区域数)、h(居民数)及每个居民的b, e, t。 - 按右端点
e升序排序所有区间。 - 遍历每个区间:
- 统计区间内已种树数量
cnt(通过遍历区间扫描used数组)。 - 若
cnt < t,从右向左遍历区间,遇到空闲位置就种树(标记used[j]=true),并增加答案,直到满足数量。
- 统计区间内已种树数量
- 输出总种树数。
时间复杂度 :最坏 O(h × n),约 5000×30000=1.5e8,在实际数据下可 AC(因为区间重叠多,扫描次数远小于最坏值)。
空间复杂度:O(n)。
代码实现
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=30010;//最大区域数
struct Node{
int b,e,t;
}a[5010];
bool used[N];//标记每个位置是否已种树
bool cmp(Node x,Node y){
return x.e<y.e;//按右端点升序
}
int main(){
int n,h;cin>>n>>h;//n:区域数,h:居民数
for(int i=1;i<=h;i++)cin>>a[i].b>>a[i].e>>a[i].t;
sort(a+1,a+h+1,cmp);//区间排序
int ans=0;//总植树数
for(int i=1;i<=h;i++){//处理每个区间
int b=a[i].b,e=a[i].e,t=a[i].t;
int cnt=0;//统计区间内已有树数
for(int j=b;j<=e;j++)if(used[j])cnt++;//扫描统计
for(int j=e;j>=b&&cnt<t;j--){//从右向左补种
if(!used[j]){//空闲位置
used[j]=true;//种树
cnt++;//已种数增加
ans++;//总树数加1
}
}
}
cout<<ans<<endl;//输出最少树木个数
return 0;
}
功能分析
- 输入解析 :读取区域总数、居民个数以及每个居民要求的区间
[b,e]和至少种树数t。 - 排序预处理:按区间右端点升序排列,确保后续处理中,靠右的树木能覆盖更多未处理的区间。
- 贪心补种 :
- 对于当前区间,先统计该区间内已种树的数量(通过遍历
used数组)。 - 若数量不足,则从区间最右端开始向左寻找空闲位置,每找到一个就种一棵树,直到满足该区间需求。
- 对于当前区间,先统计该区间内已种树的数量(通过遍历
- 结果输出:累加所有种下的树木,输出最小值。
各种学习资料,助力大家一站式学习和提升!!!
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"########## 一站式掌握信奥赛知识! ##########";
cout<<"############# 冲刺信奥赛拿奖! #############";
cout<<"###### 课程购买后永久学习,不受限制! ######";
return 0;
}
【秘籍汇总】(完整csp信奥赛C++学习资料):
1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):
https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:
https://edu.csdn.net/course/detail/40437 点击跳转

https://edu.csdn.net/course/detail/41081 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:
CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转
CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转
信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转
4、csp信奥赛冲刺一等奖有效刷题题解:
CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新): https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转
信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转
5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转
· 文末祝福 ·
cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"跟着王老师一起学习信奥赛C++";
cout<<" 成就更好的自己! ";
cout<<" csp信奥赛一等奖属于你! ";
return 0;
}