瑞瑞的装备强化
【题目来源】
瑞学堂:瑞瑞的装备强化
【题目描述】
瑞瑞在冒险中获得了 \(n\) 件装备,第 \(i\) 件装备的初始强化等级为 \(v_i\)。为了应对强大的敌人,他需要对装备进行强化。
瑞瑞有两种强化方式:
- 单件强化:选择一件装备,将其强化等级提升 \(1\)。每次单件强化花费 \(c\) 金币。
- 批量强化:选择一段连续的装备 \([l,r]\),将这段装备中所有强化等级的最小值找出来,然后将这段装备中所有等于该最小值的装备的强化等级提升 \(1\)。每次批量强化花费 \(m\) 金币。
瑞瑞希望最终所有装备的强化等级均不低于目标值 \(t\),且总花费尽可能少。请你帮他计算最少需要花费多少金币。
【输入】
第一行四个整数 \(n,t,c,m\),分别表示装备数量、目标强化等级、单件强化花费、批量强化花费。
第二行 \(n\) 个非负整数 \(v_1,v_2,…,v_n\),表示初始强化等级。
【输出】
输出一个整数,表示达到目标所需的最小金币花费。
【输入样例】
3 3 2 3
1 2 1
【输出样例】
6
【核心思想】
-
问题分析:给定 \(n\) 件装备,初始等级 \(v_i\),目标等级 \(t\)。单件强化花费 \(c\)(单点 \(+1\)),批量强化花费 \(m\)(将区间最小值及所有等于最小值的装备 \(+1\))。求使所有装备等级 \(\geq t\) 的最小总花费。这是一个排序后枚举分界 + 贪心策略问题,关键在于发现:最优策略中,批量强化总是作用于当前全局最小值所在的连续段,且可将装备按初始等级排序后枚举"单件/批量"分界点。
-
算法选择:
- 排序(Sort):将 \(v_i\) 从小到大排序,使得"前 \(i\) 件用单件、后 \(n-i\) 件用批量"的贪心策略具有单调性
- 前缀和优化:用 \(sv[i]\) 存储前 \(i\) 件初始等级之和,实现 \(O(1)\) 计算单件强化总次数
- 枚举分界点:枚举前 \(i\) 件装备用单件强化提升到 \(v_i\),后 \(n-i\) 件(及前 \(i\) 件)用批量强化从 \(v_i\) 提升到 \(t\)
-
关键步骤:
- 读入与排序:读入 \(n, t, c, m\) 和数组 \(v[1..n]\),将 \(v\) 从小到大排序
- 前缀和预处理:计算 \(sv[i] = \sum_{j=1}^{i} v_j\)
- 初始化答案:
- 方案一(全单件):\(ans = c \cdot (n \cdot t - sv[n])\)
- 方案二(全批量):\(ans = \min(ans, m \cdot (t - v_1))\)
- 枚举分界点 \(i\)(\(1\) 到 \(n\),跳过重复 \(v_i\)):
- 前 \(i\) 件单件花费:\(cost\_single = c \cdot (i \cdot v_i - sv[i])\)(将前 \(i\) 件从各自初始值提升到 \(v_i\))
- 后 \(n-i\) 件批量花费:\(cost\_batch = m \cdot (t - v_i)\)(从 \(v_i\) 批量提升到 \(t\))
- 更新 \(ans = \min(ans, cost\_single + cost\_batch)\)
- 输出 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),枚举 \(O(n)\)
- 空间复杂度:\(O(n)\),存储数组 \(v\) 和 \(sv\)
-
排序 + 贪心枚举的核心思想:
- 排序创造单调分界:排序后,前 \(i\) 件是初始等级最低的装备,它们与全局最小值的差距最大,最需要单件强化"追赶";后 \(n-i\) 件接近最小值,适合批量强化统一提升
- 批量强化的全局性:批量强化作用于"区间最小值",若所有装备等级相同则批量强化可全局生效。因此策略是:先用单件强化让一部分装备"对齐"到某个等级,再用批量强化统一提升
- 分界点的经济学意义:\(v_i\) 是单件强化能达到的"对齐等级",枚举 \(i\) 即枚举不同的对齐等级,计算对应的总成本
- 跳过重复优化:相同 \(v_i\) 的分界点产生的成本相同,跳过避免冗余计算
- 适用于"两种操作成本不同,需决策何时切换操作类型"的资源分配问题,核心在于排序后利用单调性将二维决策降为一维枚举
【算法标签】
贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 将int定义为long long,避免花费计算溢出
const int N = 100005; // 定义数组最大容量为100005
int n, t, c, m, ans; // n为装备数量,t为目标强化等级,c为单件强化花费,m为批量强化花费,ans记录最小金币花费
int v[N], sv[N]; // v[i]为第i件装备的初始强化等级(排序后);sv[i]为前i件装备初始等级的前缀和
signed main() // 使用signed main配合#define int long long
{
cin >> n >> t >> c >> m; // 读入装备数量、目标等级、单件花费、批量花费
for (int i = 1; i <= n; i++) // 读入每件装备的初始强化等级
cin >> v[i];
sort(v + 1, v + n + 1); // 将初始强化等级从小到大排序,便于后续枚举分界点
// 计算前缀和:sv[i] = v[1] + v[2] + ... + v[i]
for (int i = 1; i <= n; i++)
sv[i] = sv[i - 1] + v[i];
// 方案一:全部使用单件强化
// 每件装备从v[i]提升到t,需要(t-v[i])次单件强化,每次花费c
// 总花费 = c * Σ(t - v[i]) = c * (n*t - Σv[i])
ans = c * n * t - sv[n];
// 方案二:全部使用批量强化(从最小值v[1]开始批量提升到t)
// 批量强化花费 = m * (t - v[1]),每次批量提升最小值1级
ans = min(ans, m * (t - v[1]));
// 方案三:混合策略——前i件(较小的)用单件强化,后n-i件(较大的)用批量强化
// 枚举分界点i:前i件装备初始等级较低,用单件强化提升到v[i];后n-i件用批量强化从v[i]提升到t
for (int i = 1; i <= n; i++) // 枚举分界点i
{
if (i > 1 && v[i] == v[i - 1]) // 跳过重复的初始等级(避免重复计算相同情况)
continue;
int cnt = i; // 前i件装备用单件强化
int sum_small = sv[i]; // 前i件装备的初始等级之和
// 前i件装备从各自初始等级提升到v[i]的单件强化花费
// 第j个装备(0<=j<i)需要提升(v[i] - v[j])次
// 总次数 = i*v[i] - Σv[j] = cnt*v[i] - sum_small
// 总花费 = c * (cnt * v[i] - sum_small)
int cost_single = c * (cnt * v[i] - sum_small);
// 后n-i件装备(以及前i件提升到v[i]后)用批量强化从v[i]提升到t的花费
// 总花费 = m * (t - v[i])
int cost_batch = m * (t - v[i]);
ans = min(ans, cost_single + cost_batch); // 更新最小花费
}
cout << ans << endl; // 输出达到目标所需的最小金币花费
return 0;
}
【运行结果】
3 3 2 3
1 2 1
6
浙公网安备 33010602011771号