2018年NOIP普及组完善程序题2—右侧第一个更大值

问题:右侧第一个更大值

对于一个1到n的排列P(即1到n中每一个数在P中出现了恰好一次),令qi为第i个位置之后第一个比Pi值更大的位置,如果不存在这样的位置,则qi = n+ 1。举例来说,如果n = 5且P为1 5 4 2 3,则q为2 6 6 5 6。

下列程序读入了排列P,使用双向链表求解了答案。试补全程序。

#include 

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;
        (1);
    }
    for(int i=1;i<=n;i++)
    {
        R[i]=(2);
        L[i]=i-1;
    }
    for(int i=1;i<=n;i++)
    {
        L[(3)]=L[a[i]];
        R[L[a[i]]]=R[(4)];
    }
    for(int i=1;i<=n;i++)
    {
        cout<<(5)<<" ";
    }
    cout<

解析

对于一个1到n的排列P(即1到n中每一个数在P中恰好出现一次), 令q_i为第i个位之后第一个比p_i值更大的位置,如果不存在这样的位置,则q_i=n+1

数据范围:1\leq n\leq 100000

分析

  • p为1到n的一个排列,例如: 1 5 4 2 3
  • p里每个元素都一个位置,从1到n,例如5的位置是2,4的位置是3
  • 对于p里任何一个元素p_i,往右边找第一个比它大的元素,记录该元素的位置,如果这个位置不存在,就记录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序列。

这个算法简单直接,算法复杂度为O(n^2),其中对某元素的查找算法是O(n),枚举序列P的每个元素调用查找算法也是O(n),乘起来为O(n^2)

但对于此题的最大数据范围100000来说会TLE。

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
  • ...

子问题二:删除最小值,在剩下里面找

这个就是问题里提到的双向链表了,每个位置都记录一个左位置和右位置,当某个位置查找完成后,把它的左位置和右位置连起来就表示这个位置被删除了。

例如:1 5 3 2 4,假设现在从值2开始查找,注意值2的位置(值为4)在值3的位置(值为3)后面

2018年NOIP普及组完善程序题2—右侧第一个更大值_第1张图片

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

2018年NOIP普及组完善程序题2—右侧第一个更大值_第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. 找值1(位置2)的右侧位置,结果是位置2(值为5),然后删除值1
  2. 找值2(位置4)的右侧位置,结果是位置5(值为4),然后删除值2
  3. 找值3(位置3)的右侧位置,结果是位置5(值为4),然后删除值3
  4. 找值4(位置5)的右侧位置,结果是位置6(值为6),然后删除值4
  5. 找值5(位置2)的右侧位置,结果是位置6(值为6),然后删除值5

操作后的结果为:

2018年NOIP普及组完善程序题2—右侧第一个更大值_第3张图片

可以发现,对于位置i的右侧位置R[i],即为最后答案。例如:R[1]=2,R[2]=6, R[3]=5,R[4]=5,R[5]=6。

最终代码如下:

#include 

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<

 

你可能感兴趣的:(信息学奥赛)