题解:AtCoder AT_awc0089_b Connecting Pipes

【题目来源】

AtCoder:B - Connecting Pipes

【题目描述】

Takahashi has \(N\) pipes. The length of the \(i\)-th pipe is \(A_i\).

Some pipes are rusted. If the \(i\)-th pipe is rusted, \(B_i = 1\); if it is not rusted, \(B_i = 0\). Due to rust, the effective usable length of a rusted pipe is reduced to \(\max(A_i - C, 0)\). The effective length of a non-rusted pipe is \(A_i\).

Takahashi freely selects at least 1 and at most \(N\) of these \(N\) pipes (each pipe can be selected at most once) and connects them in a line to form a single pipe. If only one pipe is selected, its effective length directly becomes the length of the finished pipe. If two or more pipes are selected, joint parts are used to connect the pipes, so the finished pipe's length is reduced by \(K\) for each connection point.

If the number of selected pipes is \(M\), there are \(M - 1\) connection points, so the length of the finished pipe is given by the following formula:

\((\text{sum of effective lengths of the } M \text{ selected pipes}) - (M - 1) \times K\)

Takahashi wants the length of the finished pipe to be at least \(D\). When the combination and number of pipes are chosen optimally, find the maximum possible length of the finished pipe. If the maximum value is at least \(D\), output that value; if it is impossible to achieve a length of \(D\) or more regardless of the selection, output \(-1\).

高桥有 \(N\) 根管道。第 \(i\) 根管道的长度为 \(A_i\)

有些管道生锈了。如果第 \(i\) 根管道生锈了,则 \(B_i = 1\);如果没有生锈,则 \(B_i = 0\)。由于生锈,生锈管道的有效可用长度减少为 \(\max(A_i - C, 0)\)。未生锈管道的有效长度为 \(A_i\)

高桥从这 \(N\) 根管道中自由选择至少 1 根且至多 \(N\)(每根管道最多被选择一次),并将它们连接成一条线,形成一根单一的管道。如果只选择一根管道,其有效长度直接成为成品管道的长度。如果选择两根或多根管道,则需要使用接头来连接管道,因此成品管道的长度会因每个连接点而减少 \(K\)

如果选择的管道数量为 \(M\),则有 \(M - 1\) 个连接点,因此成品管道的长度由以下公式给出:

\((\text{所选 } M \text{ 根管道的有效长度之和}) - (M - 1) \times K\)

高桥希望成品管道的长度至少为 \(D\)。当最优地选择管道的组合和数量时,求成品管道的最大可能长度。如果最大值至少为 \(D\),输出该值;如果无论如何选择都无法达到 \(D\) 或以上的长度,输出 \(-1\)

【输入】

\(N\) \(D\) \(K\) \(C\)
\(A_1\) \(B_1\)
\(A_2\) \(B_2\)
\(\vdots\)
\(A_N\) \(B_N\)

  • The first line contains the number of pipes \(N\), the required length \(D\), the length reduction per connection point \(K\), and the length reduction due to rust \(C\), separated by spaces.
  • From the 2nd line to the \((N + 1)\)-th line, the information for each pipe is given.
  • The \((1 + i)\)-th line contains the length \(A_i\) of the \(i\)-th pipe and \(B_i\) (\(0\) or \(1\)) indicating whether it is rusted, separated by spaces.

【输出】

If the maximum possible length of the finished pipe is at least \(D\), output that maximum value on a single line. If it is impossible to achieve a length of at least \(D\), output \(-1\) on a single line.

【输入样例】

4 15 3 4
10 0
8 1
7 0
6 1

【输出样例】

15

【核心思想】

  1. 问题分析:给定 \(N\) 根管道,每根有原始长度 \(A_i\) 和是否生锈标志 \(B_i\)。生锈管道的有效长度为 \(\max(A_i - C, 0)\),未生锈为 \(A_i\)。选择 \(M\) 根管道连接,成品长度为(有效长度之和)\(-(M-1) \times K\)。要求最大化成品长度,若最大值 \(\geq D\) 则输出该值,否则输出 \(-1\)。这是一个贪心排序 + 前缀最优问题,关键在于将连接惩罚转化为每根管道的"净贡献"后按收益降序选取。

  2. 算法选择

    • 收益转化:将连接惩罚 \(K\) 分摊到每根额外选取的管道上,转化为每根管道的净收益
    • 降序贪心:按净收益从高到低依次选取,前缀和的最大值即为最优解
  3. 关键步骤

    • 预处理有效长度
      • 遍历 \(i\)\(1\)\(N\)
        • \(B_i = 1\)(生锈):eff_i = \max(A_i - C, 0)
        • \(B_i = 0\)(未生锈):eff_i = A_i
    • 计算单根净贡献
      • 选择 \(M\) 根管道的总长度 = \(\sum_{i=1}^{M} eff_i - (M-1) \times K\)
      • 重写为:\(K + \sum_{i=1}^{M} (eff_i - K)\)
      • 定义第 \(i\) 根管道的净收益:p_i = eff_i - K
      • 第一根管道带来基础收益 \(K\)(无连接惩罚),后续每根带来净收益 \(p_i\)
    • 降序排序:将所有 \(p_i\) 按从大到小排序
    • 贪心累加求最大前缀和
      • 初始化 sum = K(只选一根时的基础长度)
      • 遍历排序后的 \(p_i\)\(1\)\(N\)
        • sum += p_i:模拟依次选取第 \(i\) 根管道
        • maxn = \max(maxn, sum):记录所有前缀和中的最大值
    • 判断输出:若 maxn \geq D 输出 maxn,否则输出 \(-1\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),预处理 \(O(N)\),排序 \(O(N \log N)\),贪心遍历 \(O(N)\)
    • 空间复杂度:\(O(N)\),存储有效长度/净收益数组
  5. 贪心排序的核心思想

    • 惩罚转化:将连接点惩罚 \((M-1) \times K\) 转化为每根额外管道的"机会成本" \(K\),使问题变为选取哪些管道能使 \(\sum (eff_i - K)\) 最大
    • 交换论证:若存在最优解中某根管道的净收益小于未选中的某根,交换后总长度不会减少,因此按净收益降序选取必为最优
    • 前缀最优性:由于每多选一根管道增加一个连接点(固定成本 \(K\)),总长度关于选取数量的函数是单峰的,最大值出现在某个前缀位置
    • 负收益截断:若 \(p_i \leq 0\),继续选取只会使总长度减小或不变,但遍历过程中 max 操作会自动处理,无需显式截断
    • 适用于"选择子集 + 固定连接成本"的组合优化问题,通过降序贪心将指数级枚举降为线性对数级

【算法标签】

贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 定义长整型别名,便于处理大数据
#define int long long

// 定义数组最大容量
const int N = 100005;

// 全局变量声明
int n;              // 物品数量
int d;              // 目标阈值
int k;              // 基础收益
int c;              // 成本抵扣值
int maxn = -1e9;    // 记录最大累计收益,初始化为极小值
int p[N];           // 存储每个物品的实际收益

// 主函数入口(使用signed避免与long long冲突)
signed main()
{
    // 读取物品数量、目标阈值、基础收益和成本抵扣值
    cin >> n >> d >> k >> c;

    // 读取每个物品的信息并计算实际收益
    for (int i = 1; i <= n; i++)
    {
        int a, b;           // a: 原始收益, b: 物品类型标志
        cin >> a >> b;
        if (b == 1)         // 如果是特殊类型物品,扣除成本c
            a = max(0LL, a - c);    // 收益不能为负数
        p[i] = a;           // 存储实际收益
    }

    // 将实际收益按降序排序(优先选取高收益物品)
    sort(p + 1, p + n + 1, greater<int>());

    // 贪心选择:依次选取收益最高的物品
    int sum = k;            // 初始收益为基础收益k
    for (int i = 1; i <= n; i++)
    {
        sum += p[i] - k;    // 选取第i个物品后的累计收益变化
        maxn = max(maxn, sum);  // 更新最大累计收益
    }

    // 判断是否达到目标阈值
    if (maxn >= d)
        cout << maxn << endl;   // 输出最大累计收益
    else
        cout << -1 << endl;     // 无法达到目标,输出-1

    return 0;
}

【运行结果】

4 15 3 4
10 0
8 1
7 0
6 1
15
posted @ 2026-07-04 09:22  团爸讲算法  阅读(7)  评论(0)    收藏  举报