题解:学而思编程 数组推导

【题目来源】

学而思编程:数组推导

【题目描述】

已知数组 \(a\) 和数组 \(b\) 是都是由 \(n\) 个非负整数组成的数组,数组 \(b\) 中每一个元素 \(b_i\) 为数组 \(a\) 中前 \(i\) 个元素的最大值,如表达式所示:\(b_i = max\{a_1, a_2, \ldots, a_i\}\)

我们用 \(sum = a_1 + a_2 + \ldots + a_n\) 表示数组 \(a\)\(n\) 个元素之和。

现在告诉你 \(b\) 数组,请你根据 \(b\) 数组反推 \(a\) 数组。\(a\) 数组可能并不唯一,请你根据 \(a\) 数组的所有可能情况给出 \(sum\) 的最小值和最大值分别是多少。

【输入】

第一行一个整数 \(n\)

第二行 \(n\) 个整数 \(b_1, b_2, \ldots, b_n\)

【输出】

一行两个空格隔开的整数。

【输入样例】

6
0 0 5 5 10 10

【输出样例】

15 30

【核心思想】

  1. 问题分析:已知数组 \(b\) 满足 \(b_i = \max(a_1, a_2, \ldots, a_i)\),即 \(b\)\(a\) 的前缀最大值数组。要求根据 \(b\) 反推所有可能的 \(a\),并求 \(sum(a)\) 的最小值和最大值。这是一个前缀最大值逆推 + 极值分析问题,关键在于理解 \(b\) 的非降性质以及 \(a_i\) 的取值约束。

  2. 算法选择

    • 最大值分析\(a_i\) 最大可取 \(b_i\),此时 \(sum = \sum_{i=1}^{n} b_i\)
    • 最小值分析\(a_i\) 在满足前缀最大值约束下取最小值,仅在 \(b_i\) 首次出现时取 \(b_i\),其余位置取 \(0\)
  3. 关键步骤

    • 初始化:读取 \(n\) 和数组 \(b[1..n]\)
    • 计算最大值
      • mx = 0
      • 遍历 \(i\)\(1\)\(n\)
        • mx += b[i]:每个 \(a_i\) 最大取 \(b_i\),总和即为所有 \(b_i\) 之和
    • 计算最小值
      • mn = 0
      • 遍历 \(i\)\(1\)\(n\)
        • \(b_i \neq b_{i-1}\)\(b_i\) 首次出现,即前缀最大值上升):
          • mn += b[i]:该位置 \(a_i\) 必须取 \(b_i\) 才能使前缀最大值达到 \(b_i\)
        • 否则(\(b_i = b_{i-1}\),前缀最大值不变):
          • \(a_i\) 可取 \(0\)(最小),不增加总和
    • 输出 mnmx
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n)\),两次线性遍历
    • 空间复杂度:\(O(n)\),存储数组 \(b[1..n]\)
  5. 前缀最大值逆推的核心思想

    • \(b\) 的非降性:由定义 \(b_i = \max(b_{i-1}, a_i)\) 可知 \(b\) 单调不减,且 \(b_i \geq a_i\)
    • 最大值构造:令所有 \(a_i = b_i\),显然满足 \(b_i = \max(a_1, \ldots, a_i)\),此时 \(sum\) 最大
    • 最小值构造:当 \(b_i = b_{i-1}\) 时,说明前 \(i-1\) 个元素中已有值为 \(b_i\) 的元素,因此 \(a_i\) 可取 \(0\) 而不影响前缀最大值;仅当 \(b_i > b_{i-1}\) 时,\(a_i\) 必须取 \(b_i\) 才能使前缀最大值上升
    • 约束传递\(b_i\) 的变化点唯一确定了 \(a\) 中必须非零的位置,其余位置均可取 \(0\)
    • 适用于"前缀最值逆推 + 极值构造"类问题,通过分析变化点确定强制取值位置

【算法标签】

模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, a[N], mx, mn;
signed main()
{
    cin >> n;  // 输入序列长度
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];  // 输入序列
        mx += a[i];   // 计算最大值:所有元素之和
    }

    // 计算最小值
    for (int i = 1; i <= n; i++)
    {
        if (a[i] != a[i - 1])  // 如果当前元素与前一个不同
        {
            mn += a[i];  // 加入最小值
        }
    }

    cout << mn << " " << mx << endl;  // 输出最小值最大值
    return 0;
}

【运行结果】

6
0 0 5 5 10 10
15 30
posted @ 2026-07-04 14:49  团爸讲算法  阅读(6)  评论(0)    收藏  举报