1 条题解
-
0
棍子排列 详细题解
题目回顾
给定整数 ()和一个长度为 的字符串 ,由字符
<和>组成。
需要构造一个排列 (每个数恰好是 的排列),满足:- 若 ,则 必须小于前面所有元素,即 。
- 若 ,则 必须大于前面所有元素,即 。
题目保证一定有解,输出任意合法排列。
核心观察
从最后一个位置 开始逆向推导:
- 若 ,则 必须比前面所有元素都小,因此只能是当前可选数字中最小的那个。
在最终排列中,这个最小值只能是 (如果还没被使用)。推广:在逆向构造过程中,我们总是在已选中一些数后,为当前位置选择一个“极值”(最小或最大)。 - 若 ,则 必须比前面所有元素都大,只能选当前可选数字中最大的那个,即 。
我们移除已确定的 ,将问题规模减小到前 个元素。此时前面元素的值域变为去掉已选数字后的集合。反复应用这个原则即可确定所有 。
逆向构造算法
朴素做法
维护剩余可选数字集合 ,初始为 。
从 递减到 :- 若 ,则 ,然后从 中删除该值。
- 若 ,则 ,然后从 中删除该值。
最后剩下的一个元素就是 。
这样做的时间复杂度为 (每次找极值和删除需要 ),对于 已经足够,但可以优化到 。优化:双指针维护剩余值域
由于初始集合是一段连续区间 ,每次删除极值后,剩余集合仍然是一段连续区间。
因此可以用两个指针 和 表示当前剩余值域 :- 删最小值 →
l++ - 删最大值 →
r--
具体步骤:
- 初始化 。
- 逆序遍历 (下标 从 到 ):
- 若 ,则 ,然后 。
- 若 ,则 ,然后 。
- 循环结束后, 和 相等,此时 。
该算法仅用 时间,且无需额外空间(除了存储排列的数组)。
正确性证明
引理:对于任意的 (从后往前看),当我们处理到第 个位置时,剩余未分配的数字一定是某一连续区间。
证明:初始为 ,每次移除的是当前区间的左端点或右端点,剩余区间显然仍连续。引理:上述方法构造的排列满足题目条件。
证明:考虑第 个位置的元素 。- 若 ,我们设定 ,即当前剩余数的最小值。由于前缀 只会在后续步骤中使用大于 的数字(因为 已被移除),因此前缀所有元素都 ,满足小于前缀的条件。
- 若 ,我们设定 (当前最大值),前缀元素将全部来自 ,因此均小于 ,满足大于前缀的条件。
根据数学归纳法,最终排列合法。
示例运行
以 为例:
-
初始 ,逆序处理 的字符
>,<,<,<? 注意字符串 长度 4:索引 3,2,1,0(从 0 开始)。
具体:, , , 。
从 递减到 。- , → ,
- , → ,
- , → ,
- , → , (此时循环结束,实际 等于最后剩下的 ,但这里我们在循环中已经设置了 ?注意标准程序循环到 ,并将 最后赋值为 。这里我们演示:循环结束后 ,。但我们分配的顺序: 先被设置,最后 。同时我们实际得到的序列是 。按上述: 但还需要 。若按循环 时设置 ,最后 。这样得到 。让我们检查: 要求 ?但 满足; 要求 即 满足; 要求 即 满足; 要求 即 满足。得到排列 ?我们结果是 。有些偏差因为对索引的理解。正确解释:标准代码中循环
for (int i = n - 2; i >= 0; i--)设置的是a[i + 1]。因此当 时, 从 3 到 0,依次设置 。按照例子 ,即 $s_0=\texttt{<}, s_1=\texttt{<}, s_2=\texttt{>}, s_3=\texttt{<}$。逆序:$i=3 \texttt{<} \to a_4 = l = 1; i=2 \texttt{>} \to a_3 = r = 5; i=1 \texttt{<} \to a_2 = l = 2; i=0 \texttt{<} \to a_1 = l = 3$。最后 。得到 ,正是样例输出。完美。</li> </ol> </li> </ul>代码实现详解
#include <bits/stdc++.h> using namespace std; void test() { int n; cin >> n; string s; cin >> s; int l = 1; int r = n; vector<int> a(n); for (int i = n - 2; i >= 0; i--) { if (s[i] == '<') { a[i + 1] = l; l++; } if (s[i] == '>') { a[i + 1] = r; r--; } } a[0] = l; for (int i = 0; i < n; i++) { cout << a[i] << " "; } cout << '\n'; } int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; for (int i = 0; i < t; i++) { test(); } return 0; }```
- 1
信息
- ID
- 6756
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 2
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者