CSP202203C.计算资源调度器

今天我们来看CSP202203C.计算资源调度器这道题目

题意分析

本题要求我们模拟一个云计算平台的资源调度过程。平台有 n 个计算节点,分布在 m 个可用区中。需要按照启动顺序为计算任务分配计算节点,每个任务可能有以下三种约束条件:

  1. 计算节点亲和性:任务必须在指定可用区上运行
  2. 计算任务亲和性:任务必须和指定应用的任务在同一可用区运行
  3. 计算任务反亲和性:任务不能和指定应用的任务在同一节点运行(分为必须满足和尽量满足)

分配规则:

  • 先过滤出所有满足要求的节点
  • 如果无可用节点且反亲和性是"尽量满足",则去掉反亲和性约束重新过滤
  • 在候选节点中选择任务数最少、编号最小的节点

解题思路

1. 数据结构设计

  • 使用结构体存储每个节点的信息:所在可用区、任务数量、运行的应用集合
  • 使用 unordered_map<int, unordered_set<int>> 记录每个应用运行在哪些可用区
  • 使用 vector<vector<int>> 按可用区分组节点,便于快速筛选

2. 核心算法

过滤阶段

  • 根据节点亲和性确定初始候选池(指定可用区或全部节点)
  • 依次检查任务亲和性和反亲和性约束
  • 如无候选节点且反亲和性为"尽量满足",去掉该约束重新过滤

排序阶段

  • 遍历候选节点,选择任务数最少的节点
  • 任务数相同时选择编号最小的节点

3. 优化技巧

  • 使用 unordered_set 代替 set,将查找复杂度从 O(log n) 降至 O(1)
  • 根据节点亲和性预先缩小候选范围
  • 合并过滤和排序阶段,避免重复遍历
  • 使用快速 I/O 提升读写速度

代码实现

cpp 复制代码
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <climits>

using namespace std;

struct Node {
    int zone;
    int task_count;
    unordered_set<int> apps;  // 该节点上运行的应用集合
    
    Node() : zone(0), task_count(0) {}
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int n, m;
    cin >> n >> m;
    
    vector<Node> nodes(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> nodes[i].zone;
        nodes[i].task_count = 0;
    }
    
    // 按可用区分组节点
    vector<vector<int>> zone_nodes(m + 1);
    for (int i = 1; i <= n; i++) {
        zone_nodes[nodes[i].zone].push_back(i);
    }
    
    // 记录每个应用运行在哪些可用区
    unordered_map<int, unordered_set<int>> app_zones;
    
    int g;
    cin >> g;
    
    while (g--) {
        int f, a, na, pa, paa, paar;
        cin >> f >> a >> na >> pa >> paa >> paar;
        
        vector<int> result;
        result.reserve(f);
        
        for (int task = 0; task < f; task++) {
            int selected_node = 0;
            int min_tasks = INT_MAX;
            
            // 确定候选节点范围
            vector<int>* candidate_pool;
            vector<int> all_nodes;
            
            // 根据节点亲和性确定初始候选池
            if (na != 0) {
                candidate_pool = &zone_nodes[na];
            } else {
                // 所有节点
                all_nodes.reserve(n);
                for (int i = 1; i <= n; i++) {
                    all_nodes.push_back(i);
                }
                candidate_pool = &all_nodes;
            }
            
            // 第一次尝试:应用所有约束
            for (int node_id : *candidate_pool) {
                bool ok = true;
                
                // 计算任务亲和性
                if (pa != 0) {
                    if (app_zones[pa].find(nodes[node_id].zone) == app_zones[pa].end()) {
                        ok = false;
                    }
                }
                
                // 计算任务反亲和性
                if (ok && paa != 0 && nodes[node_id].apps.count(paa)) {
                    ok = false;
                }
                
                if (ok) {
                    // 选择任务数最少、编号最小的节点
                    if (nodes[node_id].task_count < min_tasks ||
                        (nodes[node_id].task_count == min_tasks && 
                         (selected_node == 0 || node_id < selected_node))) {
                        min_tasks = nodes[node_id].task_count;
                        selected_node = node_id;
                    }
                }
            }
            
            // 如果没有找到且反亲和性是"尽量满足"
            if (selected_node == 0 && paa != 0 && paar == 0) {
                // 去掉反亲和性约束重新查找
                min_tasks = INT_MAX;
                for (int node_id : *candidate_pool) {
                    bool ok = true;
                    
                    // 计算任务亲和性
                    if (pa != 0) {
                        if (app_zones[pa].find(nodes[node_id].zone) == app_zones[pa].end()) {
                            ok = false;
                        }
                    }
                    
                    if (ok) {
                        if (nodes[node_id].task_count < min_tasks ||
                            (nodes[node_id].task_count == min_tasks && 
                             (selected_node == 0 || node_id < selected_node))) {
                            min_tasks = nodes[node_id].task_count;
                            selected_node = node_id;
                        }
                    }
                }
            }
            
            // 如果找到节点,更新状态
            if (selected_node != 0) {
                nodes[selected_node].task_count++;
                nodes[selected_node].apps.insert(a);
                app_zones[a].insert(nodes[selected_node].zone);
            }
            
            result.push_back(selected_node);
        }
        
        // 输出结果
        for (int i = 0; i < result.size(); i++) {
            if (i > 0) cout << " ";
            cout << result[i];
        }
        cout << "\n";
    }
    
    return 0;
}

复杂度分析

  • 时间复杂度:O(g × f × n),其中 g 是任务组数,f 是每组任务数,n 是节点数。在实际运行中,由于使用了候选池优化和哈希表,实际复杂度会更低
  • 空间复杂度:O(n + m),用于存储节点信息、可用区分组和应用分布信息

总结

本题是一个典型的资源调度问题,关键在于:

  1. 正确理解并实现三种约束条件的过滤逻辑
  2. 处理"尽量满足"的反亲和性约束
  3. 合理使用数据结构优化查询效率
  4. 注意边界条件和状态更新的时机

通过这道题可以加深对哈希表、贪心算法和模拟类问题的理解和应用能力。

相关推荐
shada10 小时前
Boost.Process v2 在旧内核上报 `assign: Bad file descriptor`
c++
泡海椒10 小时前
规则引擎对比:JQuick-Java vs Drools 轻量场景选型对比
java·开发语言
泡泡鱼(敲代码中)10 小时前
Python 容器类型学习笔记:列表、元组、字典、集合
开发语言·笔记·python·pycharm
别动我齐刘海10 小时前
ROS2 Jazzy + C++ 实战路线——基础学习2
c++·人工智能·vscode·python·学习·机器学习·机器人
2501_9336707910 小时前
平台招商运营校招能力模型:数据分析、SQL与增长场景拆解
开发语言
菜鸟~noob23311 小时前
【Bonjour】华为Peerium架构深度解析:从冯·诺依曼到百万处理器“一台计算机”,附MATLAB性能仿真【含matlab代码,可直接运行】
开发语言·matlab·华为·peerium
朝朝辞暮i11 小时前
C++第 2 课
c++
代码中介商11 小时前
C++ 预约系统实战(三):服务端实现——libevent 事件驱动与业务路由
c++·json·c/s
白远山11 小时前
24小时自助健身房系统开发实战:从需求分析到完整指南
java·开发语言·数据库·数据挖掘·需求分析
布莱克60512 小时前
C++中的内存分区详解
c++