题解:洛谷 P8902 Range Reconstruction

【题目来源】

洛谷:P8902 [USACO22DEC] Range Reconstruction S - 洛谷

【题目描述】

Bessie 有一个数组 \(a_1,\dots,a_N\),其中 \(1\le N\le 300\) 并对于所有 \(i\)\(0\le a_i\le 10^9\)。她不会告诉你数组 \(a\) 本身,但她会告诉你 \(a\) 的每个子数组的全距。也就是说,对于每对索引 \(i\le j\),Bessie 告诉你 \(r_{i,j}=max\ a[i...j]-min\ a[i...j]\)。给定这些 \(r\) 的值,构造一个可以作为 Bessie 的原始数组的数组。你的数组中的数值应在 \([-10^9,10^9]\) 范围内。

【输入】

输入的第一行包含 \(N\)

以下 \(N\) 行,第 \(i\) 行包含整数 \(r_{i,i},r_{i,i+1},\dots,r_{i,N}\)

输入保证存在某个数组 \(a\),其中的数值在 \([0,10^9]\) 范围内,满足对于所有的 \(i\le j\),有 \(r_{i,j}=max\ a[i...j]-min\ a[i...j]\)

【输出】

输出一行,包含 \(N\) 个整数 \(b_1,b_2,\dots,b_N\),在 \([-10^9,10^9]\) 范围内,表示你的数组。这些数需要满足对于所有的 \(i\le j\)\(r_{i,j}=max\ a[i...j]-min\ a[i...j]\)

【输入样例】

3
0 2 2
0 1
0

【输出样例】

1 3 2

【核心思想】

  1. 问题分析:给定 \(N \times N\) 的上三角矩阵 \(r_{i,j}\)\(i \leq j\)),表示子数组 \([i,j]\) 的全距(最大值减最小值)。要求构造一个数组 \(ans[1..N]\),使得所有子数组的全距与给定 \(r\) 一致。本质上是递推构造问题:利用相邻元素的全距关系,逐步确定每个新元素的位置(在当前区间最大值的上方或下方)。

  2. 算法选择

    • 递推构造:设 \(ans[1] = 0\)\(ans[2] = ans[1] + r_{1,2}\),然后逐个确定 \(ans[3]\)\(ans[N]\)
    • 区间扩展判定:通过比较 \(r_{last-1,i}\)\(r_{last-1,last} + r_{last,i}\) 的关系,判断新元素 \(ans[i]\) 应在 \(ans[last]\) 的同侧还是异侧
  3. 关键步骤

    • 读取数据:读入 \(N\)\(r_{i,j}\)\(1 \leq i \leq j \leq N\)
    • 初始化\(ans[1] = 0\)\(ans[2] = ans[1] + r_{1,2}\)\(last = 2\)
    • 递推构造\(i = 3\)\(N\)):
      • \(r_{last-1,i} = r_{last-1,last} + r_{last,i}\)
        • \(ans[i]\)\(ans[last]\)\(ans[last-1]\)同侧
        • \(ans[last] > ans[last-1]\)\(ans[i] = ans[last] + r_{last,i}\)
        • 否则 \(ans[i] = ans[last] - r_{last,i}\)
      • \(r_{last-1,i} < r_{last-1,last} + r_{last,i}\)
        • \(ans[i]\)\(ans[last]\)\(ans[last-1]\)异侧
        • \(ans[last] > ans[last-1]\)\(ans[i] = ans[last] - r_{last,i}\)
        • 否则 \(ans[i] = ans[last] + r_{last,i}\)
      • \(r_{i-1,i} \neq 0\),更新 \(last = i\)
    • 输出 \(ans[1..N]\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^2)\),读取矩阵 + 线性递推
    • 空间复杂度:\(O(N^2)\),存储 \(r\) 矩阵
  5. 递推构造的核心思想

    • 全距的三角不等式:对任意 \(last-1 < last < i\),有 \(r_{last-1,i} \leq r_{last-1,last} + r_{last,i}\),等号成立当且仅当 \(ans[i]\)\(ans[last]\)\(ans[last-1]\) 同侧
    • 极值点追踪\(last\) 始终指向当前已构造区间的极值点(最大值或最小值),新元素相对于 \(last\) 的位置由全距关系确定
    • 零全距的处理\(r_{i-1,i} = 0\) 表示 \(ans[i] = ans[i-1]\),此时不更新 \(last\)(极值点不变)
    • 构造的正确性:每次扩展保证 \([last-1, i]\)\([last, i]\) 的全距约束,通过归纳法可证全局正确
    • 适用于矩阵重构、区间性质、递推构造类问题

【解题思路】

【算法标签】

普及+ #构造

【代码详解】

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

const int maxN = 305;
int a[maxN][maxN], ans[maxN];

int main()
{
    int n;
    cin >> n;  // 输入n
    for (int i=1; i<=n; i++) {  // 输入所有的数
        for (int j=i; j<=n; j++) {
            cin >> a[i][j];
        }
    }

    ans[1] = 0;  // 构造ans[1]为0
    ans[2] = ans[1] + a[1][2];  // 计算ans[2]
    int last = 2;  // 设置前一个数的下标
    for (int i=3; i<=n; i++) {  // i从3开始遍历至n
        if (a[last-1][i] == a[last-1][last] + a[last][i]) {  // 如a[1][3]==a[1][2]+a[2][3]
            if (ans[last] > ans[last-1]) {  // 且ans[2]>ans[1]
                ans[i] = ans[last] + a[last][i];  // ans[3] = ans[2]+a[2][3]
            } else {
                ans[i] = ans[last] - a[last][i];  // ans[3] = ans[2]-a[2][3]
            }
        } else if (a[last-1][i] < a[last-1][last] + a[last][i]) {  // 如a[1][3]<a[1][2]+a[2][3]
            if (ans[last] > ans[last-1]) {  // 且ans[2]>ans[1]
                ans[i] = ans[last] - a[last][i];  // ans[3] = ans[2]-a[2][3]
            } else {
                ans[i] = ans[last] + a[last][i];  // ans[3] = ans[2]+a[2][3]
            }
        }
        if (a[i-1][i] != 0) {  // 如果a[i-1][i]==0,则不更新last
            last = i;
        }
    }
    for (int i=1; i<=n; i++) {  // 输入ans数组
        cout << ans[i] << " ";
    }
    cout << endl;
    return 0;
}

【运行结果】

3
0 2 2
0 1
0
0 2 1
posted @ 2026-08-17 22:49  团爸讲算法  阅读(5)  评论(0)    收藏  举报