51nod 1272 最大距离

题目网站:http://class.51nod.com/Challenge/Problem.html#problemId=1272

一、题目描述

给出一个长度为N的整数数组A,对于每一个数组元素,如果他后面存在大于等于该元素的数,则这两个数可以组成一对。
每个元素和自己也可以组成一对。例如:{5, 3, 6, 3, 4, 2},可以组成11对,如下(数字为下标):
(0,0), (0, 2), (1, 1), (1, 2), (1, 3), (1, 4), (2, 2), (3, 3), (3, 4), (4, 4), (5, 5)。其中(1, 4)是距离最大的一对,距离为3。

输入描述

第1行:1个数N,表示数组的长度(2 <= N <= 50000)。
第2 - N + 1行:每行1个数,对应数组元素Ai(1 <= Ai <= 10^9)。

输出描述

输出最大距离。

样例输入

6
5
3
6
3
4
2

样例输出

3

二、解题思路

这道题发明了一个方法(也可以用单调栈 / 单调队列来做),我自己起了个名字叫做 “ 前缀赛梯法 ” 。很好玩吧,但是是个有意义的名字
这个方法也不能叫做方法吧,但可以称之为技巧。
 
能作为这两个数中的前一个数要满足的要求:前面没有小于他的
(我们把能满足这个要求的所有数字都存到A数组里)
 
能作为这两个数中的后一个数要满足的要求:后面没有大于他的
(我们把能满足这个要求的所有数字都存到B数组里)
 
相信大家读题的时候也已经发现了,我们要找的是他后面所有比这个数大于或等于的数。
我们不能比较附近挨着的两个,我们得把所有的都比较一遍,我给大家建议的方法就是:
 
一边输入也可以一边找出A数组中存什么数字好,输入完了后我们可以从N-1 —> 0 在来一次循环找出B数组里的数
 
至于怎么找呢?
 
我们可以记录下A数组里上一个数的下标,把 a[这个数] 和现在这个数进行比较,如果现在这个数小于上次存的那个数
A数组里多存一个i(我往A数组里存的都是下标),上次那个数的下标变成这次的下标。
 
A数组存数字的方法:
for(int i = 0;i < n;i++){
        cin >> a[i];
        if(prenum == 0 || a[i] < a[A[prenum-1]]){
            A[prenum++] = i;
        }
    }

1、一边输入一边存

2、prenum里面记录的是(上一次存进A数组里那个数的,在A数组里的)下标。

   如果一开始还没存数进去 或者 已经存了的那个数,却发现这次的也符合题目的要求

3、如果符合上面叙述的情况,把下标(i)存进A里面,A[prenum++] = i 

 
B数组同理。
B数组存数字的方法:
for(int i = n-1;i >= 0;i--){
    if(postnum == 0 || a[i] > a[B[postnum -1]]){
        B[postnum++] = i;
    }
}

1、从n-1开始循环。因为这次我们要从后往前找符合这个条件的:后面没有大于他的

  (我是从0开始输入的,如果你从1开始的话最好把prenum和postnum都赋值为1,这样你往A数组和B数组里存数的时候就是从下标为1开始存了)

 2、prenum里面记录的是(上一次存进B数组里那个数的,在B数组里的)下标。

   如果一开始还没存数进去 或者 已经存了的那个数,却发现这次的也符合题目的要求

 3、如果符合上面叙述的情况,把下标(i)存进B里面, B[postnum++] = i

 

这时候,A数组里肯定是降序的,B数组肯定也是降序的。大家自己思考一下这点。
 
现在开始讲输出
 
输出照常来说我们都要把A数组里的数和B数组里的数一一进行对比才能肯定哪个就是答案
可是这里我们用了一个小技巧:
假设A数组里有3个数,B数组里有3个数,正常情况来说需要判断9次,实际上这个代码可能只需要判断5次就出结果了。
而且正常情况还会TLE(超出时间限制)
 
那么我们这个不同寻常的技巧是什么?
注意:A数组里肯定是降序的,B数组肯定也是降序的
 
if(A数组里的数是小于B数组里的数的){
       ans和这次的答案取一个最大值
  A数组再往前一个数
}else{
  //A数组里的数比B数组里的数大
  B数组的数要往前一个数,看看前面的数可不可以,因为前面的数大一点
}
 
在我的代码中我为了输出定义了两个数
1、pre_idx 的意思是现在A数组查到哪个数了,如果待会的答案是可以的,使A数组往前一个数就相当于pre_idx-1
2、post_idx 的意思是现在B数组查到哪个数了,如果待会的答案不可以,使B数组往后一个数就相当于post_idx-1
long long pre_idx = 0, post_idx = postnum -1;
for(;pre_idx < prenum && post_idx >= 0;){
    if(a[A[pre_idx]] <= a[B[post_idx]]){
        ans = max(B[post_idx] - A[pre_idx], ans);
        --post_idx;
    }else{
        ++pre_idx;
    }
}

 输出的话我也自己起了个名字:AB判断法

是不是很生动:)

判断AB两个数组哪个下标会变

三、代码描述 

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
long long n, a[50010], A[50010], B[50010];
long long cnt_a=1, cnt_b=1, ans, prenum, postnum;

int main(){
    cin >> n;
    for(int i = 0;i < n;i++){
        cin >> a[i];
        if(prenum == 0 || a[i] < a[A[prenum-1]]){
            A[prenum++] = i;
        }
    }
    for(int i = n-1;i >= 0;i--){
        if(postnum == 0 || a[i] > a[B[postnum -1]]){
            B[postnum++] = i;
        }
    }
    long long pre_idx = 0, post_idx = postnum -1;
    for(;pre_idx < prenum && post_idx >= 0;){
        if(a[A[pre_idx]] <= a[B[post_idx]]){
            ans = max(B[post_idx] - A[pre_idx], ans);
            --post_idx;
        }else{
            ++pre_idx;
        }
    }
    cout << ans << endl;
    return 0;
}

 

posted @ 2020-07-07 22:34  elisa02  阅读(185)  评论(0编辑  收藏  举报