1 条题解

  • 0
    @ 2025-12-2 9:26:55

    问题分析

    这是一个资源分配的最优化问题。我们需要:

    1. 购买一些计算机(每台有 cic_i 个核心,频率 fif_i,价格 viv_i
    2. 接受一些订单(每个需要 CjC_j 个核心,最低频率 FjF_j,支付 VjV_j
    3. 使得总利润(订单收入 - 计算机成本)最大

    核心约束

    • 分配给订单的核心必须来自已购买的计算机
    • 每个核心的频率必须不低于订单要求的最低频率
    • 核心不能被重复分配给不同订单

    关键观察

    1. 频率排序的重要性

    由于高频核心可以用于低频订单,但低频核心不能用于高频订单,所以我们应该:

    • 将所有计算机和订单按频率降序排序
    • 这样处理时,当前处理到的计算机频率 ≥ 当前处理到的订单频率要求

    2. 统一处理计算机和订单

    我们可以将计算机和订单视为两种不同的“物品”:

    • 计算机:提供 cic_i 个核心,花费 viv_i(负价值)
    • 订单:消耗 CjC_j 个核心,获得 VjV_j(正价值)

    3. 问题转化

    问题转化为:在按频率降序处理物品时,决定购买哪些计算机,接受哪些订单,使得:

    • 任何时候已提供核心数 ≥ 已消耗核心数
    • 总价值最大

    算法设计

    状态定义

    dp[i][j]dp[i][j] 表示处理前 ii 个物品(计算机或订单)后,当前可用的核心数量为 jj 时的最大利润。

    参数范围

    • n,m2000n, m \le 2000ci,Cj50c_i, C_j \le 50
    • 最大核心数:(2000+2000)×50=200000(2000 + 2000) \times 50 = 200000(但实际不会同时需要这么多)

    转移方程

    对于每个物品(计算机或订单):

    1. 如果是计算机 (c,f,v)(c, f, v)

      • 购买:dp[i][j+c]=max(dp[i][j+c],dp[i1][j]v)dp[i][j + c] = \max(dp[i][j + c], dp[i-1][j] - v)
      • 不购买:dp[i][j]=max(dp[i][j],dp[i1][j])dp[i][j] = \max(dp[i][j], dp[i-1][j])
    2. 如果是订单 (C,F,V)(C, F, V)

      • 接受(需要 jCj \ge C):dp[i][jC]=max(dp[i][jC],dp[i1][j]+V)dp[i][j - C] = \max(dp[i][j - C], dp[i-1][j] + V)
      • 不接受:dp[i][j]=max(dp[i][j],dp[i1][j])dp[i][j] = \max(dp[i][j], dp[i-1][j])

    滚动数组优化

    由于 ii 维度可达 40004000jj 维度可达 200000200000,直接存储会 MLE。 观察到:

    • 核心总数不超过 n×50=100000n \times 50 = 100000(只考虑计算机提供的核心)
    • 可以使用滚动数组优化空间

    实现细节

    1. 数据预处理

    struct Item {
        int type;      // 0: 计算机, 1: 订单
        int cores;     // c_i 或 C_j
        int value;     // v_i(负)或 V_j(正)
        int freq;      // f_i 或 F_j
    };
    
    // 按频率降序排序,频率相同时计算机在前(先买后卖)
    bool cmp(const Item& a, const Item& b) {
        if (a.freq != b.freq) return a.freq > b.freq;
        return a.type < b.type;  // 计算机(0) < 订单(1)
    }
    

    2. DP 初始化

    const int MAX_CORES = 2000 * 50 + 5;  // 最多100000个核心
    const long long INF = 1e18;
    
    vector<long long> dp(MAX_CORES, -INF);
    dp[0] = 0;  // 初始有0个核心,利润为0
    

    3. DP 转移

    for (auto& item : items) {
        vector<long long> new_dp = dp;  // 不选当前物品
        
        if (item.type == 0) {  // 计算机
            for (int j = 0; j < MAX_CORES; j++) {
                if (dp[j] > -INF) {
                    int new_cores = j + item.cores;
                    if (new_cores < MAX_CORES) {
                        new_dp[new_cores] = max(new_dp[new_cores], dp[j] + item.value);
                    }
                }
            }
        } else {  // 订单
            for (int j = item.cores; j < MAX_CORES; j++) {
                if (dp[j] > -INF) {
                    int new_cores = j - item.cores;
                    new_dp[new_cores] = max(new_dp[new_cores], dp[j] + item.value);
                }
            }
        }
        
        dp = new_dp;
    }
    

    4. 答案提取

    最终答案是 max(dp[0],dp[1],...,dp[MAXCORES1])\max(dp[0], dp[1], ..., dp[MAX_CORES-1]),因为最终可以有剩余核心。

    复杂度分析

    • 时间复杂度O((n+m)×max_cores)O((n+m) \times \text{max\_cores}),其中 max_cores=n×50\text{max\_cores} = n \times 50
    • 空间复杂度O(max_cores)O(\text{max\_cores}),使用滚动数组优化

    对于 n=2000n=2000,最坏情况 2000×50=1000002000 \times 50 = 100000 个核心,时间复杂度约 4000×100000=4×1084000 \times 100000 = 4 \times 10^8,在优化下可以通过。

    优化技巧

    1. 核心数上界优化

    实际上,我们最多只需要处理到所有计算机核心数总和:

    int max_total_cores = 0;
    for (auto& item : items) {
        if (item.type == 0) max_total_cores += item.cores;
    }
    max_total_cores = min(max_total_cores, MAX_CORES - 1);
    

    2. 负值优化

    由于购买计算机是负收益,我们可以将 dpdp 数组初始化为负无穷,只从可行的状态转移。

    3. 边界处理

    // 订单转移时,确保 j - C >= 0
    if (j >= item.cores && dp[j] > -INF) {
        new_dp[j - item.cores] = max(new_dp[j - item.cores], dp[j] + item.value);
    }
    

    完整算法流程

    1. 输入:读取 nn 台计算机和 mm 个订单
    2. 构建物品列表:将计算机和订单合并,标记类型
    3. 排序:按频率降序,频率相同时计算机在前
    4. DP初始化dp[0]=0dp[0] = 0,其他为负无穷
    5. DP转移
      • 计算机:正向遍历(完全背包)
      • 订单:反向遍历(01背包)
    6. 提取答案dpdp 数组中的最大值

    样例分析

    样例输入:

    计算机: (4,2200,-700), (2,1800,-10), (20,2550,-9999), (4,2000,-750)
    订单: (1,1500,300), (6,1900,1500), (3,2400,4550)
    

    排序后:

    1. 计算机: (20,2550,-9999)
    2. 订单: (3,2400,4550)
    3. 计算机: (4,2200,-700)
    4. 订单: (6,1900,1500)
    5. 计算机: (4,2000,-750)
    6. 计算机: (2,1800,-10)
    7. 订单: (1,1500,300)

    最优解:

    • 购买计算机: (4,2200,-700), (4,2000,-750) → 成本1450
    • 接受订单: (1,1500,300), (6,1900,1500) → 收入1800
    • 利润: 1800 - 1450 = 350

    总结

    本题的难点在于:

    1. 将计算机和订单统一处理
    2. 理解按频率排序的重要性
    3. 设计合适的状态表示和转移方程
    4. 处理大规模的核心数(优化空间和时间)

    关键点

    • 降序排序确保高频物品先处理
    • 将计算机视为负价值的物品,订单视为正价值的物品
    • 使用滚动数组优化空间
    • 注意转移方向(计算机正向,订单反向)

    通过以上方法,可以在 O(n×max_cores)O(n \times \text{max\_cores}) 的时间复杂度内解决问题,满足题目约束条件。

    • 1

    信息

    ID
    5731
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    2
    已通过
    1
    上传者