右侧第一个更大值
对于一个1到𝑛的排列𝑃(即1到𝑛中每一个数在𝑃中出现了恰好一次),令𝑞𝑖为第𝑖个位置之后第一个比𝑃𝑖值更大的位置,如果不存在这样的位置,则𝑞𝑖 = 𝑛 +1。举例来说,如果𝑛 = 5且𝑃为1 5 4 2 3,则𝑞为2 6 6 5 6。
下列程序读入了排列𝑃,使用双向链表求解了答案。试补全程序。(第二空2 分,其余3 分)
对于一个1到n的排列P(即1到n中每一个数在P中恰好出现一次), 令为第个位之后第一个比值更大的位置,如果不存在这样的位置,则。
数据范围:。
分析
p为1到n的一个排列,例如: 1 5 4 2 3
p里每个元素都一个位置,从1到n,例如5的位置是2,4的位置是3
对于p里任何一个元素,往右边找第一个比它大的元素,记录该元素的位置,如果这个位置不存在,就记录n+1(最后一个元素的下一个位置)
| 位置 | 1 | 2 | 3 | 4 | 5 |
| p | 1 | 5 | 4 | 2 | 3 |
| 往右查找 | 5,位置2 | 无,位置6 | 无,位置6 | 3,位置5 | 无,位置6 |
| q | 2 | 6 | 6 | 5 | 6 |
1. 朴素算法
查找算法:从该元素右边第一个元素开始往右循环,依次比较,找到比它大的就退出循环,记录位置;循环结束都没找到,记录n+1的位置。
循环序列的每个元素,都调用上面的查找算法,将找到的位置(或n+1)保存到q序列。
这个算法简单直接,算法复杂度为,其中对某元素的查找算法是,枚举序列P的每个元素调用查找算法也是,乘起来为。
但对于此题的最大数据范围100000来说会超时
2. 优化分析
对于某个元素,往右查找第一个大于它的数,都需要循环比较。例如:1 5 3 2 4,对于3这个元素,需要往右循环比较,一直找到4。
但如果我们知道要找的元素就是序列最小的,往右就不需要循环比较。例如:1 5 3 2 4,对于1这个元素(最小),那右侧不管是什么都比它大。
从这个角度出发有一个优化思路:
- 每次从当前最小值出发去查找
- 它的右侧位置肯定就是第一个大于它的值。
- 然后把该最小值删掉,下次从剩下的最小值出发去查找。
按照这样的思路,算法的复杂度会变成,因为每个元素右侧第一个大于它的查找算法就不需要循环了。
于是,问题转化为两个子问题:
- 有没有办法每次对当前最小值进行查找
- 怎么把最小值删掉,在剩下里面再找
子问题一:对当前最小值查找
注意题目描述,P是1到n的一个排列,也就是说,1到n的每个数都被放到了P的某个位置上,位置也刚好是1到n。
预先建立一个数组a,用来存储1到n的值在P序列中的位置。然后对值从1到n进行枚举,例如:
假设P为1 5 3 2 4,数组a={1,4,3,5,2}。
1在P里面的位置是1那么位置2的值(5)肯定比1大。
删除掉1
枚举2,在P序列的位置是4,那么位置5的值(4)肯定比2大。
删除掉2
枚举3,在P序列的位置是3,那么位置5的值(4)肯定比3大。
删除掉3
枚举4,在P序列的位置是5,后面没有值,指向位置6。
删除掉4
枚举5,在P序列的位置是2,后面没有值,指向位置6。
删除掉5
子问题二:删除最小值,在剩下里面找
这个就是问题里提到的双向链表了,每个位置都记录一个左位置和右位置,当某个位置查找完成后,把它的左位置和右位置连起来就表示这个位置被删除了。
例如:1 5 3 2 4,假设现在从值2开始查找,注意值2的位置(值为4)在值3的位置(值为3)后面

位置4(值为2)的右边位置是5(值为4),当对位置4查找右侧第一个大的值时,位置5就是第一个;然后把值2删除。

下一步对值3查找右侧大的值时,显然值3的右侧值4肯定也大于3。
3. 算法描述
P=1 5 3 2 4,a={1,4,3,5,2},结果为q = 2 6 5 5 6

找值1(位置2)的右侧位置,结果是位置2(值为5),然后删除值1
找值2(位置4)的右侧位置,结果是位置5(值为4),然后删除值2
找值3(位置3)的右侧位置,结果是位置5(值为4),然后删除值3
找值4(位置5)的右侧位置,结果是位置6(值为6),然后删除值4
找值5(位置2)的右侧位置,结果是位置6(值为6),然后删除值5
可以发现,对于位置i的右侧位置R[i],即为最后答案。例如:R[1]=2,R[2]=6, R[3]=5,R[4]=5,R[5]=6。
代码
#include<iostream> using namespace std; const int N =100010; int n; int L[N], R[N],a[N]; int main() { cin >> n; for (int i = 1; i <= n; ++i) { int x; cin >> x; a[x] = i; } for (int i = 1; i <= n; ++i) { R[i]= i + 1 ; L[i] = i - 1; } for (int i = 1; i <= n; ++i) { L[R[a[i]]]= L[a[i]]; R[L[a[i]]] = R[a[i]]; } for (int i = 1; i <= n; ++i) { cout <<R[i]<<" "; } cout << endl; return 0; }
作者:newcode 更多资源请关注纽扣编程微信公众号

从事机器人比赛、机器人等级考试、少儿scratch编程、信息学奥赛等研究学习

浙公网安备 33010602011771号