从廊桥分配学习优先队列

news/2025/11/2 1:38:07/文章来源:https://www.cnblogs.com/qmq2020/p/19184131

题目地址:P7913 [CSP-S 2021] 廊桥分配 - 洛谷

  • 首先考虑最朴素的做法,即枚举廊桥对国内和国际的分配,此过程时间复杂度O(n),接着问题就变成了有 i 个廊桥,有m架飞机,分别要在[ai,bi]时间段内使用一个廊桥,问最多有多少架飞机可以使用廊桥。因为有“先到先得”原则,实际上只需要先将飞机到达时间从小到大排序,再按顺序枚举每一架飞机,并看其前面的飞机都有没有使用廊桥,记录总使用数,以此判断当前飞机能否使用廊桥,最后总使用数即为答案,这个枚举的过程时间复杂度为O(n2,总复杂度为O(n3,代码如下:
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 7;
     4 struct node
     5 {
     6     int a, b;
     7 } f1[N], f2[N];
     8 int n, m1, m2, MAX = -1, v1[N], v2[N]; // v数组用于记录飞机是否能用廊桥
     9 bool cmp(node x, node y)
    10 {
    11     return x.a < y.a;
    12 }
    13 int main()
    14 {
    15     scanf("%d %d %d", &n, &m1, &m2);
    16     for (int i = 1; i <= m1; i++)
    17         scanf("%d %d", &f1[i].a, &f1[i].b);
    18     for (int i = 1; i <= m2; i++)
    19         scanf("%d %d", &f2[i].a, &f2[i].b);
    20     sort(f1 + 1, f1 + m1 + 1, cmp);
    21     sort(f2 + 1, f2 + m2 + 1, cmp);
    22     for (int k = 0; k <= n; k++)
    23     {
    24         int sum1 = k, sum2 = n - k;
    25         int ans1 = 0, ans2 = 0;
    26         memset(v1, 0, sizeof(v1));
    27         memset(v2, 0, sizeof(v2));
    28         for (int i = 1; i <= m1; i++)
    29         {
    30             int flag = 1;
    31             for (int j = 1; j <= i - 1; j++)
    32                 if (f1[j].b > f1[i].a && v1[j])
    33                     flag++;
    34             if (flag <= sum1)
    35             {
    36                 v1[i] = 1;
    37                 ans1++;
    38             }
    39         }
    40         for (int i = 1; i <= m2; i++)
    41         {
    42             int flag = 1;
    43             for (int j = 1; j <= i - 1; j++)
    44                 if (f2[j].b > f2[i].a && v2[j])
    45                     flag++;
    46             if (flag <= sum2)
    47             {
    48                 v2[i] = 1;
    49                 ans2++;
    50             }
    51         }
    52         MAX = max(MAX, ans1 + ans2);
    53     }
    54     printf("%d", MAX);
    55     return 0;
    56 }

    只有20pts

  • 仔细思考下飞机使用廊桥的过程,我们发现对于任意一架飞机,因为“先到先得”,所以他使用第几个廊桥是固定的,当我们枚举廊桥的分配时,对一架飞机能否使用廊桥会重复判断,比如有i个廊桥时,这架飞机可以使用,那么有i+1个廊桥这架飞机当然可以使用。所以我们可以考虑记录每架飞机如果使用廊桥是使用第几个,这样就可以通过枚举飞机,来省略对廊桥分配的枚举,时间复杂度降为O(n2),代码如下:
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 7;
     4 struct node
     5 {
     6     int a, b;
     7 } f1[N], f2[N];
     8 int n, m1, m2, MAX = -1, v1[N], v2[N]; // 特别注意,这里的v含义不同,他记录的是第i个廊桥目前飞机最晚的离开时间
     9 int c1[N], c2[N];                      // 表示第i个廊桥有多少飞机使用
    10 int s1[N], s2[N];                      // 表示前i个廊桥一共能运送多少飞机
    11 bool cmp(node x, node y)
    12 {
    13     return x.a < y.a;
    14 }
    15 int main()
    16 {
    17     scanf("%d %d %d", &n, &m1, &m2);
    18     for (int i = 1; i <= m1; i++)
    19         scanf("%d %d", &f1[i].a, &f1[i].b);
    20     for (int i = 1; i <= m2; i++)
    21         scanf("%d %d", &f2[i].a, &f2[i].b);
    22     sort(f1 + 1, f1 + m1 + 1, cmp);
    23     sort(f2 + 1, f2 + m2 + 1, cmp);
    24     int k1 = 1; // 当前用了多少个廊桥
    25     for (int i = 1; i <= m1; i++)
    26     {
    27         for (int j = 1; j <= k1; j++)
    28         {
    29             if (v1[j] <= f1[i].a)
    30             {
    31                 v1[j] = f1[i].b;
    32                 c1[j]++;
    33                 break;
    34             }
    35             else if (j == k1)
    36             {
    37                 v1[++k1] = f1[i].b;
    38                 c1[k1] = 1;
    39                 break;//!!!这里一定要break,因为k1增加了,循环还会进行!!!
    40             }
    41         }
    42     }
    43     int k2 = 1; // 当前用了多少个廊桥
    44     for (int i = 1; i <= m2; i++)
    45     {
    46         for (int j = 1; j <= k2; j++)
    47         {
    48             if (v2[j] <= f2[i].a)
    49             {
    50                 v2[j] = f2[i].b;
    51                 c2[j]++;
    52                 break;
    53             }
    54             else if (j == k2)
    55             {
    56                 v2[++k2] = f2[i].b;
    57                 c2[k2] = 1;
    58                 break;
    59             }
    60         }
    61     }
    62     for (int i = 1; i <= n; i++)
    63     {
    64         s1[i] = s1[i - 1] + c1[i];
    65         s2[i] = s2[i - 1] + c2[i];
    66     }
    67     for (int i = 0; i <= n; i++)
    68         MAX = max(MAX, s1[i] + s2[n - i]);
    69     printf("%d", MAX);
    70     return 0;
    71 }

    得分95pts

  • 想想看哪里还能优化呢?枚举飞机这一步不可避免,但是我们找这架飞机要使用的廊桥,不就是编号最小的,并且满足廊桥上一架飞机的离开时间早于这架飞机的到达时间吗。看到查询“最小”,并且飞机对于每个廊桥肯定是先进先出的,应该就能想到使用优先队列,因为优先队列的插入和删除时间复杂度都是logn,那总的复杂度就是O(nlogn),用优先队列存取每个廊桥的飞机的离开时间和使用的廊桥,当下一架飞机到达时,弹出所有在他到达前离开的飞机,并将其占用廊桥重新加入可用廊桥,再把它存入队列,从可用廊桥中选一个编号最小的给他用(也就是用了两个优先队列,维护两个数据,其中廊桥编号要复用)。代码如下:
     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N = 1e5 + 7;
     4 struct node
     5 {
     6     int a, b;
     7 } f1[N], f2[N];
     8 int n, m1, m2, MAX = -1, c1[N], c2[N];
     9 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q1, q2; // 存离开时间,使用廊桥编号
    10 priority_queue<int, vector<int>, greater<int>> p1, p2;                 // 存空余廊桥
    11 int s1[N], s2[N];                                                      // 表示前i个廊桥一共能运送多少飞机
    12 bool cmp(node x, node y)
    13 {
    14     return x.a < y.a;
    15 }
    16 int main()
    17 {
    18     scanf("%d %d %d", &n, &m1, &m2);
    19     for (int i = 1; i <= m1; i++)
    20         scanf("%d %d", &f1[i].a, &f1[i].b);
    21     for (int i = 1; i <= m2; i++)
    22         scanf("%d %d", &f2[i].a, &f2[i].b);
    23     for (int i = 1; i <= n; i++)
    24     {
    25         p1.push(i);
    26         p2.push(i);
    27     }
    28     sort(f1 + 1, f1 + m1 + 1, cmp);
    29     sort(f2 + 1, f2 + m2 + 1, cmp);
    30     for (int i = 1; i <= m1; i++)
    31     {
    32         while (!q1.empty() && q1.top().first <= f1[i].a)
    33         {
    34             int t = q1.top().second;
    35             p1.push(t);
    36             q1.pop();
    37         }
    38         if (!p1.empty())//这里要特别注意,廊桥n不一定大于m1,m2,小心RE
    39         {
    40             int t = p1.top();
    41             p1.pop();
    42             q1.push(make_pair(f1[i].b, t));
    43             c1[t]++;
    44         }
    45     }
    46     for (int i = 1; i <= m2; i++)
    47     {
    48         while (!q2.empty() && q2.top().first <= f2[i].a)
    49         {
    50             int t = q2.top().second;
    51             p2.push(t);
    52             q2.pop();
    53         }
    54         if (!p2.empty())
    55         {
    56             int t = p2.top();
    57             p2.pop();
    58             q2.push(make_pair(f2[i].b, t));
    59             c2[t]++;   
    60         }
    61     }
    62     for (int i = 1; i <= n; i++)
    63     {
    64         s1[i] = s1[i - 1] + c1[i];
    65         s2[i] = s2[i - 1] + c2[i];
    66     }
    67     for (int i = 0; i <= n; i++)
    68         MAX = max(MAX, s1[i] + s2[n - i]);
    69     printf("%d", MAX);
    70     return 0;
    71 }

    得分100pts,成功AC

  • 从这道题中我们应该学到什么呢,为什么会想到使用优先队列,即“堆”这个数据结构,而不是线段树之类的呢?听听Chatgpt怎么说的
  • d93e6c451177a1453acf5ead6c7468f3

  •  

    总结一下,其实就是优先队列它将原来的数列重构,按照优先级排序成一个二叉树,因此查询全局最值只要o(1)的时间,插入和删除也只要O(logn)的时间 ,而线段树,他并没有重构原来的数列,他针对的是区间的处理,将相邻的几个节点的某个信息记录在一个我们额外增加的节点上(也就是为什么线段树数组要开4N),因此它有了很多区间处理的功能,但相应的他对元素的增加和删除(这改变了树的节点数量,树需要重构)复杂度大大增加。因此,当我们只关心一个数列中,某种特征最明显的那个数时,往往使用优先队列来维护,当我们关心数列某段的性质时,往往用线段树。


    ————————————————————————————————戛然而止

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/953133.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电子丨导通占空比

在DC-DC转换器(如Buck、Boost等开关电源)中,导通占空比(Duty Cycle) 是一个核心概念,直接决定了输出电压与输入电压的关系。它的定义和工作原理如下:1. 基本定义 导通占空比(D) 是指在一个开关周期内,开关管…

[SWPUCTF 2022 新生赛]奇妙的MD5 WP

[SWPUCTF 2022 新生赛]奇妙的MD5 WP 一上来页面让我们写一个奇妙的字符串,可能会有点蒙,查看 F12 源码也找不到什么有用的信息,我们这时去抓包一下这个网址,会发现: hint:select * from admin where password=md5…

DELL笔记本加内存的流水账 - zhang

刚好朋友送了一条笔记本内存条,虽然我的DELL笔记本平时也不做什么大的动作,总体来说内存是够用的,但有升级的机会就尝试去升级了。 在加内存条的时候遇到了几个问题,刚好是我之前不了解的电脑知识,也没找到相关的…

静电服对人体是安全的

静电服对人体是安全的🧑‍🏭 一、静电服的工作场景 静电服主要用于 防静电敏感环境,常见在:行业使用场景电子制造 SMT贴片、组装、测试、维修半导体 芯片封装、晶圆厂洁净室医疗设备 无尘净化间操作化工、油气 避…

MySQL元数据库information schema

一、概述 information_schema提供了对数据库元数据、统计信息以及有关MySQLServer信息的访问(例如:数据库名或表名、字段的数据类型和访问权限等)。该库中保存的信息也可以称为MySQL的数据字典或系统目录。 在每个M…

CSP-S 2025 差点(貌似真的)坠机记

突然发现自己成为老年选手。 省流:\([0,100]+[0,100]+[0,50]+[0,5]=[0,255]\)。 2025.9.19 明天怎么初赛了? 2025.9.20 今天怎么初赛了? 去年还做了很长时间的模拟题,今年是一套完整的题目都没做,不管了 qwq。从学…

一切的终点

这篇文章本来应该是一个月后写的,唉。 当 5 分钟切掉 T1,代码飞速跑过 T2 大样例时,我有那么一瞬间觉得自己能赢。 死磕 T3 2h+,费劲毕生所学无果,尽管最后时刻调出暴力,也无法改变这一切的结果。 梦想的舞台就在眼…

题解:AT_abc304_f [ABC304F] Shift Table

思路 首先,很容易想到枚举合法的循环节长度,然后遍历整个字符串,对必须要干活的天数进行标记,最后记循环节长度为 \(l\),标记天数为 \(cnt\),则这个循环节长度就有 \(2^{l-cnt}\) 个合法方案。 但是这样计算会导…

题解:CF1936C Pokmon Arena

考虑如何建图,首先对于 \(n\) 个人的 \(m\) 个属性我们都建一个点,表示目前第 \(j\) 个属性达到了 \(a_{i,j}\) 的代价。为了计算每个人的出场费用,我们再新建 \(n\) 个点(记其编号为 \(b_i\)),分别表示现在场上…

题解:CF983E NN country

首先思考线路只有从祖先到子孙的链的情况,对于询问的两个点 \(x\) 和 \(y\),我们肯定要先从 \(x\) 跳到它们的 LCA,再从 LCA 跳到 \(y\)。由于从 LCA 到 \(y\) 的过程和从 \(y\) 到 LCA 的过程是等价的,所以我们可…

CSP-S 2023 游记

1. 你知道吗?红绿灯倒计时的第一位的取值范围不是 0~9,而是 0~F。 半个十六进制的红绿灯。 半个有 OI 的生活。 第一次看到它,是在考完 csps2023 回家的路上,我家长确信那只是二极管失灵,但我亲眼看到它从 E,到…

软件技术基础的第二次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/25rjjc/homework/13541这个作业的目标 建立本地项目并关联远程仓库,熟悉程序设计的流程姓名-学号 熊宇彤-2023331200153码云仓库地址:https://gitee.com/…

补发周五日报10.31

所花时间:90min 今天主要学习内容主要是机器学习,上课没咋听 知识点总结 这个问题很关键,决策树是机器学习的基础算法,也是软件设计师考试中机器学习部分的高频考点!核心结论:决策树是一种基于 “分而治之” 思想…

CSP2025-S 游记

Day -2 早上写了教练的模拟赛,下午讲题。 晚上和 @Kevin_Mu 一起研究了神秘的dfs序求LCA,发现非常好写,同时学了ST表。 Day -1 努力的刷板子,但是头疼只写了一点点,然后把所有写过的板子看了看,把很久没写过的单…

langgraph-reflection

langgraph-reflection https://github.com/fanqingsong/langgraph-reflectionDescriptionArchitectureThis reflection agent uses two subagents:- A "main" agent, which is the agent attempting to solv…

学习日报11.2

所花时间:1h 代码量:0 搏客量:2 所学知识点:今天所学的内容是有关于中级软件设计师备考内容 关于上午题 由于所学知识点比较多,且杂乱,就简单叙述一下,大致是有关于有限自动机,一个叫状态图的认识,刚开始比较…

2025CSP-S游记

DAY -13 脱产了。正好还是运动会,到时候会有人来一起在机房。 DAY -12~-10 运动会,感觉好青春啊 hhh。这个气温也是迅速下降,好在机房还是冬暖夏凉。 DAY -9~-5 一个人在机房,但其实也还好,主要就是做同学的好题题…

获取网页logo图标(ico文件)

怎么获取网页logo图标的URL链接 第一种方法:最常用的方法(适用于90%的站点)是,直接在访问网址首页链接后加上上/favicon.ico,例如:https://www.baidu.com/favicon.ico 第二种方法:按F12,进入开发者模式。以wi…

题解:P6811 「MCOI-02」Build Battle 建筑大师

设 $f_i$ 为匹配到第 $i$ 为的序列个数,令 $last_{x,i}$ 表示从第 $i$ 为往前第一个出现 $x$ 的位置,可以得到转移 $f_i=\sum_{j=last_{a_i,i}}^{i-1}{f_j}$。最后答案即为 $\sum{f}$。 由于本题 $a$ 的特殊性,所有…