题解:洛谷 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
【核心思想】
-
问题分析:给定 \(N \times N\) 的上三角矩阵 \(r_{i,j}\)(\(i \leq j\)),表示子数组 \([i,j]\) 的全距(最大值减最小值)。要求构造一个数组 \(ans[1..N]\),使得所有子数组的全距与给定 \(r\) 一致。本质上是递推构造问题:利用相邻元素的全距关系,逐步确定每个新元素的位置(在当前区间最大值的上方或下方)。
-
算法选择:
- 递推构造:设 \(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]\) 的同侧还是异侧
-
关键步骤:
- 读取数据:读入 \(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\)
- 若 \(r_{last-1,i} = r_{last-1,last} + r_{last,i}\):
- 输出 \(ans[1..N]\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N^2)\),读取矩阵 + 线性递推
- 空间复杂度:\(O(N^2)\),存储 \(r\) 矩阵
-
递推构造的核心思想:
- 全距的三角不等式:对任意 \(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
浙公网安备 33010602011771号