找回密码
 立即注册
查看: 18|回复: 0

2021年CSP-S廊桥分配问题解析(洛谷P7913):基于贪心算法与优先级队列的解题思路

[复制链接]
  • 打卡等级:即来则安
  • 打卡总天数:15
  • 打卡月天数:1
  • 打卡总奖励:117
  • 最近打卡:2025-08-02 15:29:27

21

主题

5

回帖

2388

积分

VIP

积分
2388
发表于 2025-7-23 14:21:04 | 显示全部楼层 |阅读模式
截图未命名.jpg
一、题目解读
2021年CSP-S中的“廊桥分配”(洛谷P7913)是一个经典的资源分配问题。题目要求处理n个航班,每个航班有到达和离开时间,需在m1到m2个廊桥的限制下,计算使用不同数量的廊桥时能服务的**航班数。核心在于高效分配廊桥资源,避免时间冲突,同时满足数量限制。
二、解题思路
采用贪心策略与优先级队列优化
1. 航班排序:按到达时间升序排列,确保处理顺序符合时间逻辑。
2. 动态分配:使用两个优先队列——available存储当前可用廊桥的释放时间,in_use存储正在使用的廊桥及释放时间。
3. 分配逻辑:
    优先使用已释放的廊桥(时间不冲突时);
    若无可用廊桥且剩余廊桥数未满,新建分配。
4. 前缀和优化:计算各廊桥数量的前缀和,便于快速查询不同k值下的**服务数。
三、解题步骤
1. 输入与预处理:读取航班数据,按到达时间排序。
2. 初始化队列:available为空,in_use记录当前分配状态。
3. 循环分配:
    释放过期廊桥(到达时间前已完成的航班);
    若available不空,分配最小释放时间的廊桥;
    否则若廊桥数未满,新建分配。
4. 前缀和计算:累加各廊桥服务数,生成最终结果。
四、代码与注释
  1. #include <iostream>
  2. #include <vector>
  3. #include <algorithm>
  4. #include <queue>
  5. using namespace std;

  6. // 计算使用k个廊桥时能服务的**航班数
  7. vector<int> calculate_max_flights(const vector<pair<int, int>>& flights, int max_bridges) {
  8.     vector<int> res(max_bridges + 1, 0);
  9.     if (flights.empty()) return res;
  10.    
  11.     // 按到达时间排序航班
  12.     vector<pair<int, int>> sorted_flights = flights;
  13.     sort(sorted_flights.begin(), sorted_flights.end());
  14.    
  15.     // 优先队列存储可用廊桥的释放时间
  16.     priority_queue<int, vector<int>, greater<int>> available;
  17.     // 优先队列存储正在使用的廊桥的释放时间
  18.     priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> in_use;
  19.    
  20.     int count = 0;
  21.     for (const auto& flight : sorted_flights) {
  22.         int arrive = flight.first;
  23.         int depart = flight.second;
  24.         
  25.         // 释放已经完成的廊桥
  26.         while (!in_use.empty() && in_use.top().first <= arrive) {
  27.             available.push(in_use.top().second);
  28.             in_use.pop();
  29.         }
  30.         
  31.         // 如果有可用廊桥,则分配
  32.         if (!available.empty()) {
  33.             int bridge = available.top();
  34.             available.pop();
  35.             in_use.push({depart, bridge});
  36.             count++;
  37.             if (bridge <= max_bridges) {
  38.                 res[bridge]++;
  39.             }
  40.         }
  41.         // 如果没有可用廊桥但还有未分配的廊桥,则分配新的
  42.         else if (in_use.size() < max_bridges) {
  43.             int bridge = in_use.size() + 1;
  44.             in_use.push({depart, bridge});
  45.             count++;
  46.             if (bridge <= max_bridges) {
  47.                 res[bridge]++;
  48.             }
  49.         }
  50.     }
  51.    
  52.     // 计算前缀和
  53.     for (int i = 1; i <= max_bridges; ++i) {
  54.         res[i] += res[i - 1];
  55.     }
  56.    
  57.     return res;
  58. }

  59. int main() {
  60.     ios::sync_with_stdio(false);
  61.     cin.tie(nullptr);
  62.    
  63.     int n, m1, m2;
  64.     cin >> n >> m1 >> m2;
  65.    
  66.     vector<pair<int, int>> domestic_flights(n);
  67.     for (int i = 0; i < n; ++i) {
  68.         cin >> domestic_flights[i].first >> domestic_flights[i].second;
  69.     }
  70.     vector<int> max_flights = calculate_max_flights(domestic_flights, m2);
  71.    
  72.     for (int i = m1; i <= m2; ++i) {
  73.         cout << max_flights[i] << ';
  74.     }
  75.     cout << endl;
  76.    
  77.     return 0;
  78. }
复制代码


五、总结
算法通过优先级队列高效管理廊桥状态,结合贪心思想优先分配可用资源,避免回溯。前缀和的应用进一步简化了结果查询过程。时间复杂度为O(nlogn),适合处理大规模数据。关键点在于对资源释放时间的动态维护与分配优先级判断。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

SEO网站 ( 冀ICP备2025113422号-2 )|网站地图

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表