csp信奥赛C++高频考点专项训练之贪心算法 --【排序贪心】:魔法

题目描述
cjwssb 知道是误会之后,跟你道了歉。你为了逗笑他,准备和他一起开始魔法。不过你的时间不多了,但是更惨的是你还需要完成 n n n 个魔法任务。假设你当前的时间为 T T T,每个任务需要有一定的限制 t i t_i ti 表示只有当你的 T T T 严格大于 t i t_i ti 时你才能完成这个任务,完成任务并不需要消耗时间。当你完成第 i i i 个任务时,你的时间 T T T 会加上 b i b_i bi,此时要保证 T T T 在任何时刻都大于 0 0 0,那么请问你是否能完成这 n n n 个魔法任务,如果可以,输出 +1s \texttt{+1}\texttt{s} +1s,如果不行,输出 -1s \texttt{-1}\texttt{s} -1s。
输入格式
第一行:一个整数 Z Z Z,表示有 Z Z Z 个测试点。
对于每个测试点:
第一行:两个整数 n , T n,T n,T,表示有 n n n 个任务,你一开始有 T T T 的时间。
接下来 n n n 行,每行 2 2 2 个数字, t i t_i ti 与 b i b_i bi。
输出格式
对于每个测试点,输出 +1s \texttt{+1}\texttt{s} +1s 或者 -1s \texttt{-1}\texttt{s} -1s。
输入输出样例 1
输入 1
1
2 13
1 -9
5 -3
输出 1
+1s
说明/提示
对于 20 % 20\% 20% 的数据, n ≤ 10 n\leq10 n≤10;
对于 100 % 100\% 100% 的数据, n ≤ 10 5 , Z ≤ 10 , t i ≤ 10 5 , T ≤ 10 5 , − 10 5 ≤ b i ≤ 10 5 n\leq10^5,Z\leq10,t_i\leq10^5,T\leq10^5,-10^5\leq b_i\leq 10^5 n≤105,Z≤10,ti≤105,T≤105,−105≤bi≤105。
思路分析
本题要求在初始时间 T 下,按一定顺序完成 n 个任务,每个任务有门槛 t i t_i ti(需 T > t i T > t_i T>ti)和收益 b i b_i bi(可为负)。完成所有任务后输出 +1s,否则输出 -1s。
关键贪心策略
-
将任务分为两组:
- b i ≥ 0 b_i \ge 0 bi≥0 的"正收益"任务
- b i < 0 b_i < 0 bi<0 的"负收益"任务
-
正收益任务:按 t i t_i ti 升序 处理。
因为门槛越低越容易满足,且完成后续时间增加,有利于后续任务。
-
负收益任务:按 t i + b i t_i + b_i ti+bi 降序 处理。
推导:对于两个负收益任务 (i, j),先做 i 再 j 的所需最小初始时间为 max ( t i , t j − b i ) \max(t_i, t_j - b_i) max(ti,tj−bi),先 j 后 i 为 max ( t j , t i − b j ) \max(t_j, t_i - b_j) max(tj,ti−bj)。
若 t i + b i > t j + b j t_i + b_i > t_j + b_j ti+bi>tj+bj,则 max ( t i , t j − b i ) ≤ max ( t j , t i − b j ) \max(t_i, t_j - b_i) \le \max(t_j, t_i - b_j) max(ti,tj−bi)≤max(tj,ti−bj),即先 i 更优。
因此按 t i + b i t_i + b_i ti+bi 从大到小排序。
-
处理过程中时刻保证 (T > 0),且在完成负收益任务后需重新检查 (T > 0)(因为 b i < 0 b_i < 0 bi<0 可能使时间降至非正)。
复杂度
排序 O ( n log n ) O(n \log n) O(nlogn)
代码实现
cpp
#include <bits/stdc++.h>
using namespace std;
struct node { //任务结构体
int t, b; //门槛t,收益b
};
int Z, n; //Z测试点个数,n任务数
long long T; //当前时间,用long long防止正收益累加溢出
node pos[100005], neg[100005]; //正收益任务数组,负收益任务数组
int cp, cn; //正收益任务个数,负收益任务个数
int main() {
scanf("%d", &Z); //读入测试点个数
while (Z--) { //循环每个测试点
scanf("%d%lld", &n, &T); //读入n和初始时间T(注意%lld对应long long)
cp = cn = 0; //清空计数器
for (int i = 0; i < n; ++i) { //读入n个任务
int t, b;
scanf("%d%d", &t, &b);
if (b >= 0) pos[cp++] = {t, b}; //正收益存入pos数组
else neg[cn++] = {t, b}; //负收益存入neg数组
}
//正收益任务按门槛t升序排序
sort(pos, pos + cp, [](node x, node y) {
return x.t < y.t;
});
//负收益任务按(t+b)降序排序,贪心最优顺序
sort(neg, neg + cn, [](node x, node y) {
return x.t + x.b > y.t + y.b;
});
bool ok = true; //标记是否可行
//先处理所有正收益任务
for (int i = 0; i < cp; ++i) {
if (T <= pos[i].t) { //当前时间不大于门槛,无法完成
ok = false;
break;
}
T += pos[i].b; //完成正收益任务,时间增加
}
if (!ok) { //若正收益阶段已失败
printf("-1s\n"); //输出-1s并跳过负收益处理
continue;
}
//再处理所有负收益任务
for (int i = 0; i < cn; ++i) {
if (T <= neg[i].t) { //门槛不满足
ok = false;
break;
}
T += neg[i].b; //完成负收益任务,时间减少
if (T <= 0) { //完成之后时间必须大于0
ok = false;
break;
}
}
//根据ok输出结果
printf(ok ? "+1s\n" : "-1s\n");
}
return 0;
}
功能分析
- 静态数组 :用全局
pos[100005]和neg[100005]存储任务,分别记录数量cp, cn,避免使用vector。 - 分组排序 :根据
b的正负分别存入数组,并按贪心策略排序。 - 模拟执行:先做所有正收益任务,再做负收益任务。过程中任何门槛不满足或时间 ≤0 则失败。
- 输出 :通过所有任务输出
+1s,否则-1s。
各种学习资料,助力大家一站式学习和提升!!!
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;
}