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

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

关键贪心策略

  1. 将任务分为两组:

    • b i ≥ 0 b_i \ge 0 bi≥0 的"正收益"任务
    • b i < 0 b_i < 0 bi<0 的"负收益"任务
  2. 正收益任务:按 t i t_i ti 升序 处理。

    因为门槛越低越容易满足,且完成后续时间增加,有利于后续任务。

  3. 负收益任务:按 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 从大到小排序。

  4. 处理过程中时刻保证 (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;
}
相关推荐
wearegogog1231 小时前
基于和差波束法的单脉冲测角MATLAB实现
人工智能·算法·matlab
AI科技星1 小时前
灵魂商数(SQ) · 全域数学统一定义【乖乖数学】
算法·机器学习·数学建模·数据挖掘·量子计算
晓觉儿1 小时前
【GPLT】2026年第十一届团队程序设计天梯赛赛后题解(已写2h,存档中)
数据结构·c++·算法·深度优先·图论
We་ct2 小时前
LeetCode 322. 零钱兑换:动态规划入门实战
前端·算法·leetcode·typescript·动态规划
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 394. 字符串解码 | C++ 单栈回压法
c++·算法·leetcode
穿条秋裤到处跑2 小时前
每日一道leetcode(2026.04.22):距离字典两次编辑以内的单词
算法·leetcode
淘矿人2 小时前
Claude辅助算法设计与优化
人工智能·python·算法·microsoft·github·bug·pygame
流年如夢2 小时前
自定义类型进阶:联合与枚举
java·c语言·开发语言·数据结构·数据库·c++·算法
wayz112 小时前
Day 10:集成学习进阶(Boosting: AdaBoost, GBDT)
算法·机器学习·集成学习·boosting