瑞瑞的装备强化

【题目来源】

瑞学堂:瑞瑞的装备强化

【题目描述】

瑞瑞在冒险中获得了 \(n\) 件装备,第 \(i\) 件装备的初始强化等级为 \(v_i\)。为了应对强大的敌人,他需要对装备进行强化。

瑞瑞有两种强化方式:

  1. 单件强化:选择一件装备,将其强化等级提升 \(1\)。每次单件强化花费 \(c\) 金币。
  2. 批量强化:选择一段连续的装备 \([l,r]\),将这段装备中所有强化等级的最小值找出来,然后将这段装备中所有等于该最小值的装备的强化等级提升 \(1\)。每次批量强化花费 \(m\) 金币。

瑞瑞希望最终所有装备的强化等级均不低于目标值 \(t\),且总花费尽可能少。请你帮他计算最少需要花费多少金币。

【输入】

第一行四个整数 \(n,t,c,m\),分别表示装备数量、目标强化等级、单件强化花费、批量强化花费。

第二行 \(n\) 个非负整数 \(v_1,v_2,…,v_n\),表示初始强化等级。

【输出】

输出一个整数,表示达到目标所需的最小金币花费。

【输入样例】

3 3 2 3
1 2 1

【输出样例】

6

【核心思想】

  1. 问题分析:给定 \(n\) 件装备,初始等级 \(v_i\),目标等级 \(t\)。单件强化花费 \(c\)(单点 \(+1\)),批量强化花费 \(m\)(将区间最小值及所有等于最小值的装备 \(+1\))。求使所有装备等级 \(\geq t\) 的最小总花费。这是一个排序后枚举分界 + 贪心策略问题,关键在于发现:最优策略中,批量强化总是作用于当前全局最小值所在的连续段,且可将装备按初始等级排序后枚举"单件/批量"分界点。

  2. 算法选择

    • 排序(Sort):将 \(v_i\) 从小到大排序,使得"前 \(i\) 件用单件、后 \(n-i\) 件用批量"的贪心策略具有单调性
    • 前缀和优化:用 \(sv[i]\) 存储前 \(i\) 件初始等级之和,实现 \(O(1)\) 计算单件强化总次数
    • 枚举分界点:枚举前 \(i\) 件装备用单件强化提升到 \(v_i\),后 \(n-i\) 件(及前 \(i\) 件)用批量强化从 \(v_i\) 提升到 \(t\)
  3. 关键步骤

    • 读入与排序:读入 \(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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),枚举 \(O(n)\)
    • 空间复杂度:\(O(n)\),存储数组 \(v\)\(sv\)
  5. 排序 + 贪心枚举的核心思想

    • 排序创造单调分界:排序后,前 \(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
posted @ 2026-08-06 17:01  团爸讲算法  阅读(9)  评论(0)    收藏  举报