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. 注意边界条件和状态更新的时机

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

相关推荐
EXI-小洲1 小时前
Java 操作 Word:字符串替换、图片插入、动态生成表格与API接口下载
java·开发语言·spring boot·word
会周易的程序员2 小时前
给 PLC 写一个字节码虚拟机:STVM 虚拟机架构设计
开发语言·c++·虚拟机·软plc·iec61131·stvm
机器视觉知识推荐、就业指导2 小时前
为什么老说“纯 Qt 没啥就业市场”?
开发语言·qt
telepan2 小时前
Qt 开发避坑与性能指南:如何优雅、安全地定义全局常量字符串?
开发语言·qt
小灰灰搞电子2 小时前
完全驾驭 Qt 与数据库:C++ ORM 框架 QxOrm 原理与实践指南
数据库·c++·qt
QT界面美化性能优化2 小时前
QT+AI:使用AI技术为QT应用程序赋能
c++·人工智能·qt·opencv·qt教程·qt6.3
m0_695251492 小时前
Qt MaintenanceTool 使用国内镜像加速下载(超详细教程)
开发语言·qt
大衛說3 小时前
《Python 从入门到精通》系列总览与学习路线
开发语言·python·学习
学习星球4 小时前
单调栈——从“找下一个更大的“到柱状图中的最大矩形
数据库·c++·算法·leetcode·xcode