NOIP2012 提高组 - 借教室 题解 - 前缀和与差分
[NOIP2012 提高组] 借教室 题解 - 前缀和与差分/n/n## 题目描述/n/n在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。/n/n面对海量租借教室的信息,我们自然希望编程解决这个问题。/n/n我们需要处理接下来 'n' 天的借教室信息,其中第 'i' 天学校有 'ri' 个教室可供租借。共有 'm' 份订单,每份订单用三个正整数描述,分别为 'dj, sj, tj',表示某租借者需要从第 'sj' 天到第 'tj' 天租借教室(包括第 'sj' 天和第 'tj' 天),每天需要租借 'dj' 个教室。/n/n我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供 'dj' 个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。/n/n借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第 'sj' 天到第 'tj' 天中有至少一天剩余的教室数量不足 'dj' 个。/n/n现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。/n/n## 输入格式/n/n第一行包含两个正整数 'n, m',表示天数和订单的数量。/n/n第二行包含 'n' 个正整数,其中第 'i' 个数为 'ri',表示第 'i' 天可用于租借的教室数量。/n/n接下来有 'm' 行,每行包含三个正整数 'dj, sj, tj',表示租借的数量,租借开始、结束分别在第几天。/n/n每行相邻的两个数之间均用一个空格隔开。天数与订单均用从 '1' 开始的整数编号。/n/n## 输出格式/n/n如果所有订单均可满足,则输出只有一行,包含一个整数 '0'。否则(订单无法完全满足)/n/n输出两行,第一行输出一个负整数 '-1',第二行输出需要修改订单的申请人编号。/n/n## 样例 #1/n/n### 样例输入 #1/n/n/n4 3 /n2 5 4 3 /n2 1 3 /n3 2 4 /n4 2 4/n/n/n### 样例输出 #1/n/n/n-1 /n2/n/n/n## 提示/n/n【输入输出样例说明】/n/n第 '1 '份订单满足后,'4 '天剩余的教室数分别为 '0, 3, 2, 3'。第 '2' 份订单要求第 '2 '天到第 '4' 天每天提供' 3 '个教室,而第 '3' 天剩余的教室数为' 2',因此无法满足。分配停止,通知第'2' 个申请人修改订单。/n/n【数据范围】/n/n对于10%的数据,有'1≤ n,m≤ 10';/n/n对于30%的数据,有'1≤ n,m≤1000';/n/n对于 70%的数据,有'1 ≤ n,m ≤ 105';/n/n对于 100%的数据,有'1 ≤ n,m ≤ 106,0 ≤ ri,dj≤ 109,1 ≤ sj≤ tj≤ n'。/n/n/nNOIP 2012 提高组 第二天 第二题/n/n2022.2.20 新增一组 hack 数据/n用C++讲解,用前缀和与差分内容:## 解题思路/n/n题目要求判断是否所有订单都能被满足,如果不能满足则输出第一个无法满足的订单的申请人编号。/n/n首先,我们需要统计每一天可用的教室数量。可以使用一个数组 rooms 来记录每一天可用的教室数量。然后,我们遍历每一份订单,判断是否能够满足。/n/n对于每一份订单,我们需要判断从开始日期到结束日期之间的每一天,剩余的教室数量是否足够。如果有一天的剩余教室数量不足,则无法满足订单,输出该订单的申请人编号。/n/n## 算法步骤/n/n1. 读取输入,初始化变量。/n2. 统计每一天可用的教室数量,存储在数组 rooms 中。/n3. 遍历每一份订单:/n 1. 获取订单的开始日期 start 和结束日期 end,以及需要租借的教室数量 num。/n 2. 判断从 start 到 end 之间的每一天,剩余的教室数量是否足够:/n - 如果有一天的剩余教室数量不足 num,则输出该订单的申请人编号,结束程序。/n - 否则,更新每一天的剩余教室数量。/n4. 如果所有订单都能被满足,则输出 0。/n/n## 代码实现/n/ncpp/n#include <iostream>/n#include <vector>/nusing namespace std;/n/nint main() {/n int n, m;/n cin >> n >> m;/n/n vector<int> rooms(n + 1); // 存储每一天可用的教室数量/n for (int i = 1; i <= n; i++) {/n cin >> rooms[i];/n }/n/n // 计算前缀和/n for (int i = 1; i <= n; i++) {/n rooms[i] += rooms[i - 1];/n }/n/n for (int i = 0; i < m; i++) {/n int num, start, end;/n cin >> num >> start >> end;/n/n // 判断订单是否满足/n if (rooms[end] - rooms[start - 1] < num * (end - start + 1)) {/n cout << -1 << endl;/n cout << i + 1 << endl;/n return 0;/n }/n }/n/n cout << 0 << endl;/n return 0;/n}/n/n/n## 复杂度分析/n/n该算法需要遍历每一份订单,并对每一份订单的开始日期到结束日期之间的每一天进行判断。因此,算法的时间复杂度为 $O(m /cdot n)$。/n/n其中,$m$ 是订单的数量,$n$ 是天数。/n/n空间复杂度为 $O(n)$,用于存储每一天可用的教室数量。/n/n使用前缀和与差分的优化:/n/n- 我们可以使用前缀和来优化判断订单是否满足的步骤。计算 rooms 数组的前缀和, rooms[i] 表示前 'i' 天可用的教室数量总和。/n- 这样,判断从 start 到 end 之间的教室数量是否足够,只需要计算 rooms[end] - rooms[start - 1],时间复杂度为 $O(1)$。/n- 因此,使用前缀和优化后,算法的时间复杂度降低为 $O(n + m)$。/n/n注意:/n/n- rooms 数组的下标从 '1' 开始,因为我们要计算前缀和。/n- 在计算前缀和时,我们需要将 rooms[0] 初始化为 '0'。/n- 在判断订单是否满足时,我们要注意 start 和 end 的范围,避免数组越界。/n/n## 优化后的代码/n/ncpp/n#include <iostream>/n#include <vector>/nusing namespace std;/n/nint main() {/n int n, m;/n cin >> n >> m;/n/n vector<long long> rooms(n + 1); // 存储每一天可用的教室数量/n for (int i = 1; i <= n; i++) {/n cin >> rooms[i];/n }/n/n // 计算前缀和/n for (int i = 1; i <= n; i++) {/n rooms[i] += rooms[i - 1];/n }/n/n for (int i = 0; i < m; i++) {/n long long num, start, end;/n cin >> num >> start >> end;/n/n // 判断订单是否满足/n if (rooms[end] - rooms[start - 1] < num * (end - start + 1)) {/n cout << -1 << endl;/n cout << i + 1 << endl;/n return 0;/n }/n }/n/n cout << 0 << endl;/n return 0;/n}/n
原文地址: http://www.cveoy.top/t/topic/ePg3 著作权归作者所有。请勿转载和采集!