右侧第一个更大值

对于一个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这个元素(最小),那右侧不管是什么都比它大。

从这个角度出发有一个优化思路:

  • 每次从当前最小值出发去查找
  • 它的右侧位置肯定就是第一个大于它的值。
  • 然后把该最小值删掉,下次从剩下的最小值出发去查找。

按照这样的思路,算法的复杂度会变成O(n),因为每个元素右侧第一个大于它的查找算法就不需要循环了。

于是,问题转化为两个子问题:

  1. 有没有办法每次对当前最小值进行查找
  2. 怎么把最小值删掉,在剩下里面再找

子问题一:对当前最小值查找


注意题目描述,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;
}

 

posted @ 2020-07-17 17:26  new-code  阅读(837)  评论(0)    收藏  举报