1 条题解
-
0
问题分析
这是一个资源分配的最优化问题。我们需要:
- 购买一些计算机(每台有 个核心,频率 ,价格 )
- 接受一些订单(每个需要 个核心,最低频率 ,支付 )
- 使得总利润(订单收入 - 计算机成本)最大
核心约束:
- 分配给订单的核心必须来自已购买的计算机
- 每个核心的频率必须不低于订单要求的最低频率
- 核心不能被重复分配给不同订单
关键观察
1. 频率排序的重要性
由于高频核心可以用于低频订单,但低频核心不能用于高频订单,所以我们应该:
- 将所有计算机和订单按频率降序排序
- 这样处理时,当前处理到的计算机频率 ≥ 当前处理到的订单频率要求
2. 统一处理计算机和订单
我们可以将计算机和订单视为两种不同的“物品”:
- 计算机:提供 个核心,花费 (负价值)
- 订单:消耗 个核心,获得 (正价值)
3. 问题转化
问题转化为:在按频率降序处理物品时,决定购买哪些计算机,接受哪些订单,使得:
- 任何时候已提供核心数 ≥ 已消耗核心数
- 总价值最大
算法设计
状态定义
设 表示处理前 个物品(计算机或订单)后,当前可用的核心数量为 时的最大利润。
参数范围:
- ,
- 最大核心数:(但实际不会同时需要这么多)
转移方程
对于每个物品(计算机或订单):
-
如果是计算机 :
- 购买:
- 不购买:
-
如果是订单 :
- 接受(需要 ):
- 不接受:
滚动数组优化
由于 维度可达 , 维度可达 ,直接存储会 MLE。 观察到:
- 核心总数不超过 (只考虑计算机提供的核心)
- 可以使用滚动数组优化空间
实现细节
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个核心,利润为03. 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. 答案提取
最终答案是 ,因为最终可以有剩余核心。
复杂度分析
- 时间复杂度:,其中
- 空间复杂度:,使用滚动数组优化
对于 ,最坏情况 个核心,时间复杂度约 ,在优化下可以通过。
优化技巧
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. 负值优化
由于购买计算机是负收益,我们可以将 数组初始化为负无穷,只从可行的状态转移。
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); }完整算法流程
- 输入:读取 台计算机和 个订单
- 构建物品列表:将计算机和订单合并,标记类型
- 排序:按频率降序,频率相同时计算机在前
- DP初始化:,其他为负无穷
- DP转移:
- 计算机:正向遍历(完全背包)
- 订单:反向遍历(01背包)
- 提取答案: 数组中的最大值
样例分析
样例输入:
计算机: (4,2200,-700), (2,1800,-10), (20,2550,-9999), (4,2000,-750) 订单: (1,1500,300), (6,1900,1500), (3,2400,4550)排序后:
- 计算机: (20,2550,-9999)
- 订单: (3,2400,4550)
- 计算机: (4,2200,-700)
- 订单: (6,1900,1500)
- 计算机: (4,2000,-750)
- 计算机: (2,1800,-10)
- 订单: (1,1500,300)
最优解:
- 购买计算机: (4,2200,-700), (4,2000,-750) → 成本1450
- 接受订单: (1,1500,300), (6,1900,1500) → 收入1800
- 利润: 1800 - 1450 = 350
总结
本题的难点在于:
- 将计算机和订单统一处理
- 理解按频率排序的重要性
- 设计合适的状态表示和转移方程
- 处理大规模的核心数(优化空间和时间)
关键点:
- 降序排序确保高频物品先处理
- 将计算机视为负价值的物品,订单视为正价值的物品
- 使用滚动数组优化空间
- 注意转移方向(计算机正向,订单反向)
通过以上方法,可以在 的时间复杂度内解决问题,满足题目约束条件。
- 1
信息
- ID
- 5731
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者