蓝桥杯 第 2 场算法双周赛 第4题 通关【算法赛】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.mfbz.cn/a/107139.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

群晖上搭建teamspeak3语音服务器

什么是 TeamSpeak &#xff1f; TeamSpeak &#xff08;简称 TS&#xff09;是一款团队语音通讯工具&#xff0c;但比一般的通讯工具具有更多的功能而且使用方便。它由服务器端程序和客户端程序两部分组成&#xff0c;如果不是想自己架设 TS 服务器&#xff0c;只需下载客户端程…

SQL Server Management Studio (SSMS)的安装教程

文章目录 SQL Server Management Studio (SSMS)的安装教程从Microsoft官网下载SQL Server Management Studio安装程序。选中安装程序右键并选择“以管理员的身份运行”选项选择安装目录&#xff0c;单击“安装”按钮开始安装过程安装成功界面安装完成后&#xff0c;您可以启动S…

LaTeX:在标题section中添加脚注footnote

命令讲解 先导包&#xff1a; \usepackage{footmisc} 设原标题为&#xff1a; \section{标题内容} 更改为&#xff1a; \section[标题内容]{标题内容\protect\footnote{脚注内容}} 语法讲解&#xff1a; \section[]{} []内为短标题&#xff0c;作为目录和页眉中的标题。…

Java面向对象(进阶)-- this关键字的使用

文章目录 一、引子&#xff08;1&#xff09; this是什么&#xff1f;&#xff08;2&#xff09;什么时候使用this1.实例方法或构造器中使用当前对象的成员2. 同一个类中构造器互相调用 二、探讨&#xff08;1&#xff09;问题&#xff08;2&#xff09;解决 三、this关键字&am…

Android framework服务命令行工具框架 - Android13

Android framework服务命令行工具框架 - Android13 1、framework服务命令行工具简介2、cmd 执行程序2.1 目录和Android.bp2.2 cmdMain 执行入口2.3 cmd命令 3、am命令工具&#xff0c;实质脚本执行cmd activity3.1 sh脚本3.2 activity服务注册3.3 onShellCommand执行 4、简易时…

Linux 系统调用IO口,利用光标偏移实现文件复制

用系统调用IO函数实现从一个文件读取最后2KB数据并复制到另一个文件中&#xff0c;源文件以只读方式打开&#xff0c;目标文件以只写的方式打开&#xff0c;若目标文件不存在&#xff0c;可以创建并设置初始值为0664&#xff0c;写出相应代码&#xff0c;要对出错情况有一定的处…

Peter算法小课堂—归并排序

位运算 << 这个符号相当于将一个数二进制往左移动几位&#xff0c;如(100110)2<<1(001100)2。相当于乘以2的k次方 >> 这个符号相当于将一个数二进制往右移动几位&#xff0c;如(100110)2<<1(0100110)2。相当于除以2的k次方 归并排序 先看一个视频…

macOS Sonoma 14.1正式版(23B74)发布(可下载黑白苹果镜像)

系统介绍 黑果魏叔苹果今天为 macOS Sonoma 推出了 14.1 版本更新&#xff0c;魏叔发现&#xff0c;本更新主要改善了 Apple Music 界面&#xff0c;设置中新增保修状态&#xff0c;并修复了多项错误内容。 根据苹果的新说明&#xff0c;这次的 Mac 更新不仅提供了一系列的改善…

asp.net教务管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio计算机毕业设计

一、源码特点 asp.net 教务管理信息系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语言 开发 asp.net教务管理系统 应用技术&a…

数据链路层和DNS之间的那些事~

数据链路层&#xff0c;考虑的是两个节点之间的传输。这里面的典型协议也很多&#xff0c;最知名的就是“以太网”。我们本篇主要介绍的就是以太网协议。这个协议规定了数据链路层&#xff0c;也规定了物理层的内容。 目录 以太网帧格式 帧头 载荷 帧尾 DNS 从输入URL到…

(c语言进阶)字符串函数、字符分类函数和字符转换函数

一.求字符串长度 1.strlen() (1)基本概念 头文件&#xff1a;<string.h> (2)易错点&#xff1a;strlen()的返回值为无符号整形 #include<stdio.h> #include<string.h> int main() {const char* str1 "abcdef";const char* str2 "bbb&q…

Linux常见问题解决操作(yum被占用、lsb无此命令、Linux开机进入命令界面等)

Linux常见问题解决操作&#xff08;yum被占用、lsb无此命令、Linux开机进入命令界面等&#xff09; 问题一、新安装的Linux使用命令lsb_release提示无此命令&#xff0c;需先安装再使用 Linux安装lsb命令 lsb是Linux Standard Base的缩写&#xff08;Linux基本标准&#xff…

Centos7 安装和配置 Redis 5 教程

在Centos上安装Redis 5&#xff0c;如果是 Centos8&#xff0c;那么 yum 仓库中默认的 redis 版本就是 5&#xff0c;直接 yum install 即可。但如果是 Centos7&#xff0c;yum 仓库中默认的 redis 版本是 3 系列&#xff0c;比较老&#xff1a; 通过 yum list | grep redis 命…

Constellation 介绍:Chainlink 黑客马拉松

在 2020 年&#xff0c;Chainlink 举办了其第一次线上黑客马拉松。当时&#xff0c;DeFi 作为一个类别刚刚开始蓬勃发展&#xff0c;而 NFT 也只是刚刚起步。这次黑客马拉松吸引了来自 45 个国家的 1,000 多名注册参与者&#xff0c;并收到了来自 70 个项目提交。 从那时起&am…

【C++初探:简单易懂的入门指南】一

【C初探&#xff1a;简单易懂的入门指南】一 1. 命名空间1.1 命名空间的定义1.2 命名空间的使用方法 2. C的输入、输出2.1 为什么使用输入、输出要引用一个<iostream>的头文件&#xff1f;2.2 为什么代码里面开放了一个叫std的命名空间2.3 代码中出现的<<和>>…

基于SSM的航班订票管理系统的设计与实现

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

SpringBoot使用WebSocket收发实时离线消息

引入maven依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> WebScoket配置处理器 import org.springframework.boot.web.servlet.ServletContextI…

JVM面试知识点整理

文章目录 (一) JVM组成JVM组成部分和运行流程从图中可以看出 JVM 的主要组成部分运行流程&#xff1a;程序计数器Java堆虚拟机栈方法区堆栈的区别是什么&#xff1f; (二) 类加载器双亲委派模型类装载的执行过程 (三) 垃圾回收对象什么时候可以被垃圾回收哪些可以作为根对象 垃…

浅谈安科瑞EMS能源管控平台建设的意义-安科瑞 蒋静

摘 要&#xff1a;能源消耗量大、能源运输供给不足、环境压力日趋增加、能耗双控等一系列问题一直困扰着钢铁冶金行业&#xff0c;制约着企业快速稳定健康发展。本文介绍的安科瑞EMS能源管控平台&#xff0c;采用自动化、信息化技术&#xff0c;实现从能源数据采集、过程监控、…

Spring Boot简介

Spring Boot帮助你创建可以运行的独立的、基于Spring的生产级应用程序。 我们对Spring平台和第三方库采取了有主见的观点&#xff0c;这样你就能以最少的麻烦开始工作。 大多数Spring Boot应用程序只需要很少的Spring配置。 你可以使用Spring Boot来创建Java应用程序&#xff…
最新文章