蓝桥杯 第 2 场算法双周赛 第4题 通关【算法赛】c++ 优先队列 + 小根堆 详解注释版

news/2024/5/20 7:38:35 标签: 蓝桥杯, 算法, c++, 数据结构, 优先队列,

 题目

通关【算法赛】icon-default.png?t=N7T8https://www.lanqiao.cn/problems/5889/learning/?contest_id=145

问题描述

小蓝最近迷上了一款电玩游戏“蓝桥争霸”。这款游戏由很多关卡和副本组成,每一关可以抽象为一个节点,整个游戏的关卡可以抽象为一棵树形图,每一关会有一道算法题,只有当经验值不低于第 i 关的要求 ki​ 时,小蓝才能挑战成功通过此关,并且获得 si​ 的经验值,每关的经验值只能获得一次。每个关卡(除了 11 号点),都会有一个前置关卡,只有通过了前置关卡,才能挑战后续关卡。

小蓝初始在 11 号点,也就是游戏开局初始点,同时具有一个初始经验值 P,他可以任意规划挑战顺序,他想问你最多能够挑战成功多少道题。

小蓝会告诉你关卡的所有信息,以及他的初始经验值,你需要回答他最多能够挑战成功多少关卡。

输入格式

第一行输入两个整数 n,P,表示关卡的数量以及小蓝的初始经验值。

接下来 n 行,每行输入三个整数 fi​,si​,ki​,fi​ 表示每一关的前置关卡( f1​ 一定为 00 ),si​ 表示经验值,ki​ 表示挑战成功最少需要的经验值。

输出格式

一个整数,表示在最优的规划下,最多能挑战成功的关卡数量。

样例输入

4 5
0 3 5
1 2 8
1 3 9
3 1 15

样例输出

3

说明

游戏地图如下:

关卡描述

小蓝初始点在 11 号关卡,初始经验为 55。每个关卡具有挑战前提:11 号关卡可以直接挑战,如果要挑战 22 号关卡,必须通过 11 号关卡,3,43,4号关卡类似。

小蓝的一种挑战顺序如下:

  1. 由于初始经验为 55,满足 11 号关卡要求,所以可以直接挑战成功 11 号关卡,获得 33 经验值,此时经验值为 88,并且获得挑战 2,32,3 号关卡的机会。

  2. 此时经验为 88,满足 22 号关卡要求,但是不满足 33 号要求,所以可以直接挑战成功 22 号关卡,获得 22 经验值,此时经验值为 1010。

  3. 此时经验为 1010,满足 33 号关卡要求,所以对 33 号关卡挑战成功,获得 33 经验值,此时经验值为 1313,并且获得挑战 44 号关卡的机会。

  4. 此时经验为 1313,小于 44 号关卡要求,所以无法成功挑战 44 号关卡,游戏无法继续。

评测数据范围

f1​=0<fi​≤n≤105,0≤P,si​,ki​≤109。

数据保证输入为一棵树,并且根节点为 11。

运行限制

语言最大运行时间最大运行内存
C++1s256M
C1s256M
Java2s256M
Python33s256M
PyPy33s256M

思路和解题方法

首先,定义了一些全局变量和类型别名:

  • N 定义为 1e5+100,表示任务数量的上限。
  • Pair 是一个包含两个整数的类型别名,用于存储任务的优先级和任务编号。
  • G 是一个邻接表,用于存储任务关系图,每个任务的子任务列表存储在 G 中。
  • S 数组用于存储每个任务的耗能值。
  • K 数组用于存储每个任务的优先级。
  • n 表示任务数量。
  • P 表示初始能量值。
  • cntvis 是辅助变量。

接下来是 sol 函数,用于解决任务调度问题。函数的主要逻辑如下:

  1. 读取任务数量 n 和初始能量值 P
  2. 使用循环读取每个任务的父任务编号 f、耗能值 S[i] 和优先级 K[i],并将每个任务添加到对应父任务的子任务列表中。
  3. 创建一个最小优先队列) q,用于存储任务的优先级和任务编号,按照优先级从小到大排序。
  4. 将初始任务(编号为0)的优先级和编号加入队列。
  5. 初始化完成任务的计数器 ccnt 为 -1,因为初始任务不计入完成数量。
  6. 进入循环,当队列不为空时进行以下操作:
    • 检查当前能量值是否大于等于队首任务的优先级。
    • 如果满足条件,表示能够执行任务:
      • 完成任务数量 ccnt 加一。
      • 取出队首任务,并将其从队列中弹出。
      • 将任务的能量值加到当前能量值上。
      • 遍历该任务的子任务,将子任务的优先级和编号加入队列。
    • 如果当前能量值不足以执行队首任务,跳出循环。
  7. 输出完成任务的数量 ccnt

        使用了优先队列(最小)来实现任务调度。通过不断执行优先级最高的任务,并更新能量值,直到能量值不足以执行下一个任务为止。最终输出完成的任务数量。

复杂度

        时间复杂度:

                O(n^2)

时间复杂度:

  • 读取任务数量和初始能量值的时间复杂度为 O(1)。
  • 循环读取每个任务的父任务编号、耗能值和优先级的时间复杂度为 O(n)。
  • 创建优先队列 q 的时间复杂度为 O(nlogn),其中 n 是任务数量。
  • 循环执行任务的时间复杂度取决于任务的数量和任务之间的关系。在最坏情况下,每个任务都是其他任务的子任务,因此循环执行任务的时间复杂度为 O(n^2)。但是,如果任务之间的关系是稀疏的,循环执行任务的时间复杂度可能会小于 O(n^2)。
  • 输出完成任务数量的时间复杂度为 O(1)。

总结,整个算法的时间复杂度在最坏情况下为 O(n^2),但在实际情况下可能会更好。

        空间复杂度:

                O(n)

空间复杂度:

  • 邻接表 G 的空间复杂度为 O(n),存储了任务之间的关系。
  • 数组 S 和 K 的空间复杂度为 O(n),存储了每个任务的耗能值和优先级。
  • 优先队列 q 的空间复杂度为 O(n),存储了任务的优先级和任务编号。
  • 辅助变量的空间复杂度可以忽略不计。

总结,整个算法的空间复杂度为 O(n),其中 n 是任务数量。

c++ 代码

#include <iostream>
#include <vector>
#include <queue> // 包含优先队列的头文件
using namespace std;

const int N = 1e5 + 100; // 定义任务数量的上限

typedef pair<int, int> Pair; // 定义一个包含两个整数的类型别名
vector<int> G[N]; // 邻接表,存储任务之间的关系
int S[N], K[N]; // 数组,存储每个任务的耗能值和优先级
int n, P, cnt, vis[N]; // 全局变量,表示任务数量、初始能量值、辅助变量

void sol() {
    cin >> n >> P; // 读取任务数量和初始能量值

    for (int i = 1; i <= n; i++) {
        int f;
        cin >> f >> S[i] >> K[i]; // 读取父任务编号、耗能值和优先级
        G[f].push_back(i); // 将当前任务加入对应父任务的子任务列表中
    }

    priority_queue<Pair, vector<Pair>, greater<Pair>> q; // 创建一个最小,用于存储任务的优先级和任务编号
    q.push({K[0], 0}); // 将初始任务加入队列中

    int ccnt = -1; // 完成任务的计数器,初始值为 -1,因为初始任务不计入完成数量

    while (!q.empty()) { // 当队列不为空时,循环执行任务
        auto [k, u] = q.top(); // 取出队首任务的优先级和编号
        q.pop(); // 将队首任务从队列中弹出

        if (P >= k) { // 如果当前能量值可以执行队首任务
            P += S[u]; // 将任务的能量值加到当前能量值上
            ccnt++; // 完成任务数量加一

            for (auto v : G[u]) { // 遍历该任务的子任务
                q.push({K[v], v}); // 将子任务的优先级和编号加入队列
            }
        } else { // 如果当前能量值不足以执行队首任务
            break; // 跳出循环
        }
    }

    cout << ccnt << endl; // 输出完成任务的数量
}

int main() {
    sol(); // 调用 sol 函数解决问题
    exit(0); // 终止程序
}

相关知识 和推荐视频

1. pair:  Pair 基础

  • pair 是 C++ 标准库中的一个模板类,用于存储两个值的有序对。它定义在 <utility> 头文件中。
  • pair 类模板接受两个类型参数,分别表示第一个值的类型和第二个值的类型。例如,pair<int, double> 表示一个包含一个整数和一个浮点数的有序对。
  • pair 类模板的实例可以通过花括号 {} 初始化,也可以通过构造函数进行初始化。可以使用 firstsecond 成员变量来访问有序对中的第一个值和第二个值。

2. 优先队列   小顶   的介绍 建议选着来看,看看大小是啥有啥用,就行了。 

建议跳看

 priority_queue<Pair, vector<Pair>, greater<Pair>> q;
  • priority_queue 是 C++ 标准库中的一个模板类,用于实现优先队列)。它定义在 <queue> 头文件中。
  • priority_queue 类模板接受三个类型参数,分别表示存储在队列中的元素类型、底层容器类型和比较函数对象类型。例如,priority_queue<int, vector<int>, greater<int>> 表示一个存储整数的优先队列,使用 vector 作为底层容器,并按照从小到大的顺序进行排序。
  • 在代码中,Pair 是一个包含两个整数的类型别名,vector<Pair> 表示使用 vector 作为底层容器来存储 Pair 类型的元素,greater<Pair> 表示使用 greater 函数对象来定义元素之间的比较关系。
  • greater<Pair> 是一个函数对象,它定义了对 Pair 类型的元素进行比较的方式。在这里,greater<Pair> 使用 operator() 函数重载来比较两个 Pair 类型的元素,根据 Pair 中第一个值的大小进行比较。因此,priority_queue<Pair, vector<Pair>, greater<Pair>> 创建的优先队列会按照 Pair 中第一个值的从小到大的顺序进行排序。

3.扩展

大顶

大顶可以通过使用 less 函数对象来定义元素之间的比较关系。

在 C++ 中,priority_queue 默认使用 less 函数对象来实现大顶

4.再解释一下内容

while (!q.empty()) {
        if (P >= q.top().first) { // 当当前能量值大于等于队首任务的优先级时,执行任务
            ccnt ++; // 完成任务数量加一
            Pair tmp = q.top(); // 取出队首任务
            q.pop(); // 弹出队首任务
            P += S[tmp.second]; // 将任务的能量值加到当前能量值上
            for (int v : G[tmp.second]) { // 遍历该任务的子任务
                q.push({K[v], v}); // 将子任务的优先级和编号加入队列
            }
        } else {
            break; // 当前能量值不足以执行队首任务时,跳出循环
        }
    }
  1. while (!q.empty()):当任务队列不为空时,执行循环体内的代码。

  2. if (P >= q.top().first):判断当前能量值 P 是否大于等于队首任务的优先级 q.top().first。如果是,则执行任务;否则,跳出循环。

  3. ccnt++:完成任务数量加一。

  4. Pair tmp = q.top():取出队首任务,并将其存储在临时变量 tmp 中。

  5. q.pop():弹出队首任务,将其移出任务队列。

  6. P += S[tmp.second]:将任务的能量值 S[tmp.second] 加到当前能量值 P 上,表示执行任务消耗了一定的能量。

  7. for (int v : G[tmp.second]):遍历该任务的子任务,使用范围-based for 循环,将每个子任务的编号存储在变量 v 中。

  8. q.push({K[v], v}):将子任务的优先级 K[v] 和编号 v 加入任务队列 q 中,表示将子任务加入待执行的任务队列中。

  9. else:当当前能量值不足以执行队首任务时,跳出循环。

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。


http://www.niftyadmin.cn/n/5130131.html

相关文章

计算机算法分析与设计(21)---回溯法(图着色问题)

文章目录 一、背景知识二、问题转换与描述三、算法讲解3.1 思路分析3.2 状态空间生成树3.3 代码编写 一、背景知识 1. 为地图或其他由不同区域组成的图形着色时&#xff0c;相邻国家/地区不能使用相同的颜色。 我们可能还想使用尽可能少的不同颜色进行填涂。一些简单的“地图”…

面试题-React(十八):一文学会 React Router

一、React Router简介 React Router是一个用于管理React应用中导航和路由的库。它允许开发者在单页面应用&#xff08;SPA&#xff09;中创建多个页面&#xff0c;每个页面对应一个不同的URL。React Router的主要特性包括&#xff1a; 声明式路由&#xff1a; React Router允…

【斑梨】世界最小?linux开发板?价格只要39元 Luckfox Pico Mini 超越树莓派PICO ESP32 Arduino

教程地址 幸狐Luckfox Pico RV1103 教程合集 斑梨】世界最小&#xff1f;linux开发板&#xff1f;价格只要39元 Luckfox Pico Mini 超越树莓派PICO ESP32 Arduino

MR混合现实情景实训教学系统模拟高空作业情景实训教学

高空作业是一项极具风险和挑战性的工作&#xff0c;需要从业人员具备丰富的专业知识和过硬的技能。然而&#xff0c;由于实际的高空作业环境往往具有不可控的因素&#xff0c;因此&#xff0c;传统的高空作业实训教学往往只能在模拟器上进行&#xff0c;这无疑限制了教学效果。…

VMware16,运行虚机后E盘下就会产生一个奇怪的文件夹

问题现象&#xff1a; VMware16&#xff0c;运行虚机后E盘下就会产生一个奇怪的文件夹&#xff0c;是乱码的 问题原因&#xff1a; 虚机安装路径存在中文 解决方法&#xff1a; 删除乱码文件夹 一&#xff1a;是否有中文路径&#xff0c;有的话改为英文路径 二&#xff1…

基于SSM的高校图书馆设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

二、基础算法精讲:二分

目录 1、二分查找-深入理解1.1 在排序数组中查找元素的第一个和最后一个位置 2、二分查找-习题课2.1 寻找峰值2.2 寻找旋转排序数组中的最小值2.3 寻找旋转排序数组中的最小值 II2.4 搜索旋转排序数组 1、二分查找-深入理解 Q&#xff1a;返回数组中大于等于 t a r g e t tar…

java异常类如何定义

在Java中&#xff0c;异常类通常是通过继承java.lang.Exception类或java.lang.RuntimeException类来定义的&#xff0c;具体取决于异常是受检查异常还是非受检查异常。 受检查异常&#xff08;Checked Exceptions&#xff09;&#xff1a; 受检查异常是在编译时必须捕获或声明的…