题目网站: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; }