题目描述
输入 n 个不超过109 的单调不减的(就是后面的数字不小于前面的数字)非负整数 a1,a2,…,an,然后进行m 次询问。对于每次询问,给出一个整数q,要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 −1 。
输入格式
第一行 2 个整数 n 和 m,表示数字个数和询问次数。
第二行 n 个整数,表示这些待查询的数字。
第三行 m 个整数,表示询问这些数字的编号,从 1 开始编号。
输出格式
输出一行,m 个整数,以空格隔开,表示答案。
输入输出样例
输入 #1
11 3 1 3 3 3 5 7 9 11 13 15 15 1 3 6
输出 #1
1 2 -1
说明/提示
数据保证,1≤n≤10^6,0≤ai,q≤10^9,1≤m≤10^5
本题输入输出量较大,请使用较快的 IO 方式。
-------------------------------------------------------华丽的分割线--------------------------------------------------------
一开始看到这题聪明的我立马写了一个for循环去遍历数组找出那个数。结果TLE(偷)了。我以为是死循环,改了一下又给我wa(哇)了。
”稻花香里说丰年,听取WA声一片。"宋代辛弃疾那个时间是就诞生了编程。所以,第一台电脑不在美国的宾夕法尼亚大学诞生,而是中国宋代辛弃疾发明的。果然,我就只代码猿中语文最好的
。
-------------------------------------------------------华丽的分割线--------------------------------------------------------
于是我从新整理心情,编出来了一种算法, 二分!!!
奶奶说到:“小伙,故事讲完了吗?”
好吧,上面都是编的,其实我一看这一题就能想到是二分我好聪明。毕竟它也是个模板题。
接下来我来先讲一讲二分。
-------------------------------------------------------华丽的分割线--------------------------------------------------------
首先,我演示一下从1 2 2 3 4 4 5 6 6中找出5的二分方法。
我们要二分得先要找出数组的中间的元素和下标。这时,我们要就用到L和R,分别表示左边界和右边界。那二分查找时L和R分别就是数组的开头下标1和结尾下标N。然后我们就要计算他的中间点。至于为什么要这样做,你看完就知道了。那中间点的常用词是mid,计算这个中间点记住这条公式,比其它要简单很多, mid=(l+r)/2。那在我给的这组样例里n就等于9。根据我们的公式得知mid等于5,那我们就判断下标为5的数大于等于还是小于我们要查找的那个数。
这时,下标为mid的数是4。那我们看一下4大于等于我们要查找的5还是小于我们要查找的5。结果是小于。这是,我们就知道了我们要找的数在mid的右边。因为序列是有序的,是从小到大的,所以mid右边的数都大于等于它。所以我们把L赋值为mid+1, 这是L的值为6,R的值位N,那说明我们要继续查找L到R之间那个数等于5。那这次,mid就等于(6+9)/2=7。
这时,我们要再判断一下、以mid为下标的数大于等于5还是小于5。我们要判断的这个a[mid]等于5,正好是答案,但是不要急,在二分算法里面一般是不会去专门判断答案的,同学们可以继续看下去,就知道为什么不用判断了。回到模拟,这个数5大于等于我们要找的答案(其实它就是答案)我们就把R赋值成mid,就是7。现在L=6, R=7。
然后我们继续用上面的方法。把mid赋值成(6+7)/2=6。然后下标为6的数是4,它小于我们要查找的数。于是我们把L赋值为6+1=7。
到这个时候,L和R都等于7了,不满足L<R的条件,到L<R已经不成立时,我就们需要停止执行以上步骤,也就是代码里的退出循环,因为我们每次都在执行同样的判断和同样的赋值。然后你再看,L等于什么,他就等于我们的答案。所以,二分完后L就是答案的下标。这时就有同学要问了,为什么不是R?当然,你在二分查找里面如果你while循环的条件是L<R并且L的赋值是mid+1就也可以输出R。但是在二分答案里面,会有些特殊的情况,后面我会将二分答案的。
好了二分查找讲完了,它每次都把区间分两半然后在其中一个有答案的区间里面继续找,所以这个算法叫二分。是不是理解了呢,没理解可以在评论区留言问我,我会尽量快的回答。
-------------------------------------------------------华丽的分割线--------------------------------------------------------
好了,我们回归题目再来看一下。emmmmm~,除了输入有些不一样,其它和我刚才讲的简直就是一摸一样,所现在以我们要思考的就是怎么把我刚刚说的文字转化成代码。
输入不用我多说,况且它本来就有序了,不然又要搞好多东西。然后我们边输入要查询的数字边进行二分查找。所以我把二分查找直接放函数find()里了。然后我用while循环,然后每次都判断L是不是小于R,是就继续循环,不是就退出。然后每次都判断a[mid]是否大于等于我们要查找的数。是就把R赋值为mid,否则把L赋值为mid+1,是不是和上面的一摸一样?!
接下来是撒花代码。
-------------------------------------------------------华丽的分割线--------------------------------------------------------
#include <bits/stdc++.h>
using namespace std;
int n, m, tmp, a[1000005];
int find(int x) {
int l=1, r=n;
while(l<r){
int mid=(l+r)/2;
if(a[mid]>=x){
r=mid;
}else{
l=mid+1;
}
}
return (a[l]==x ? l : -1);
}
int main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++){
scanf("%d", &a[i]);
}
for(int i=1; i<=m; i++){
scanf("%d", &tmp);
printf("%d ", find(tmp));
}
return 0;
}
本文章制作不易,希望大家能给我这个弱鸡博主点个赞和关注还有收藏,我将感激不尽。
看好了,这是给我点赞的所有粉丝。
给我点赞的人我都点赞回去了,不信就试试吧!!!
谢谢大家!!!