4.5 二分
问题 A: 找x
题目描述
输入一个数n,然后输入n个数值各不相同,再输入一个值x,输出这个值在这个数组中的下标(从0开始,若不在数组中则输出-1)。
输入
测试数据有多组,输入n(1<=n<=200),接着输入n个数,然后输入x。
输出
对于每组输入,请输出结果。
样例输入
4
1 2 3 4
3
样例输出
2
思路
这题很简单,之前做到过,是用for循环的方式遍历,这次只要改成用二分查找法就好了。
注意:这题不要用sort函数排序,虽然使用二分查找要保证单调递增或者单调递减,但是这里用了sort函数会改变下标,然后导致答案错误,估计codeup上的OJ本身输入的序列就是有序的,所以不用考虑乱序先排序的问题了。
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
int BinarySearch(int a[], int left, int right, int x){
int mid;
while(left<=right){
mid = (left+right)/2;
if(a[mid]==x) return mid;//找到了x的值,返回x的下标
else if(a[mid]>x) right = mid-1;
else left = mid+1;
}
return -1;//未找到,返回-1
}
int tmp[201]={0};
int main(){
int n;
while(scanf("%d", &n) != EOF){
for(int i=0;i<n;i++) scanf("%d", &tmp[i]);
int x;
scanf("%d", &x);
printf("%d\n", BinarySearch(tmp, 0, n-1, x));
memset(tmp, 0, sizeof(tmp));
}
return 0;
}
问题 B: 打印极值点下标
题目描述
在一个整数数组上,对于下标为i的整数,如果它大于所有它相邻的整数,或者小于所有它相邻的整数,则称为该整数为一个极值点,极值点的下标就是i。
输入
每个案例的输入如下:
有2×n+1行输入:第一行是要处理的数组的个数n;
对其余2×n行,第一行是此数组的元素个数k(4<k<80),第二行是k个整数,每两个整数之间用空格分隔。
输出
每个案例输出不多于n行:每行对应于相应数组的所有极值点下标值,下标值之间用空格分隔,如果没有极值点则不输出任何东西。
样例输入
2
4
1 2 1 3
5
3 4 5 6 7
样例输出
0 1 2 3
0 4
思路
这题也是挺简单的,虽然我想不出来怎么用二分的思想去处理……于是乎换了种常规思路来写,就是先把第一个元素和最后一个元素的特殊情况列出来判断,然后再对普遍情况判断,如果满足题设要求,返回true,否则返回false。
这里要注意一下格式问题,首先输出的每行后面不能有空格(这个很普遍,很多题目都有这样的要求),然后是没有极值点就不能输出任何东西(包括换行符),最后是让我一直格式错误50%的地方(应该细心的人不会犯这个错叭hhh):判断第一个极值点的时候要用cnt==1,我一开始写了i==0来判断,这样子的话必须第一个元素是极值点才能正常输出,否则就会开头打一个空格了。
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
bool judge(int a[], int n, int x){//传入数组名、数字长度和要查询的下标
if(x==0){//先处理第一个和最后一个的情况
if(a[x]>a[x+1]||a[x]<a[x+1]) return true;
else return false;
}
if(x==n-1){
if(a[x]>a[x-1]||a[x]<a[x-1]) return true;
else return false;
}
if(a[x]>a[x-1]&&a[x]>a[x+1]) return true;//然后处理普遍情况
else if(a[x]<a[x-1]&&a[x]<a[x+1]) return true;
else return false;
}
int tmp[80]={0};
int main(){
int n;
while(scanf("%d", &n) != EOF){
while(n--){
int k;
scanf("%d", &k);
for(int i=0;i<k;i++) scanf("%d", &tmp[i]);
int cnt = 0;
for(int i=0;i<k;i++){
if(judge(tmp, k, i)){
cnt++;//有极值点就计数点加1
if(cnt==1) printf("%d", i);//如果是第一个极值点,就直接输出
else printf(" %d", i);//否则先输出一个空格
}
}
if(cnt!=0) printf("\n");//如果有极值点就输出完之后输出一个换行,否则不输出
memset(tmp, 0, sizeof(tmp));
}
}
return 0;
}
问题 C: 查找
题目描述
输入数组长度 n
输入数组 a[1…n]
输入查找个数m
输入查找数字b[1…m]
输出 YES or NO 查找有则YES 否则NO 。
输入
输入有多组数据。
每组输入n,然后输入n个整数,再输入m,然后再输入m个整数(1<=m<=n<=100)。
输出
如果在n个数组中输出YES否则输出NO。
样例输入
6
3 2 5 4 7 8
2
3 6
样例输出
YES
NO
思路
这题就比较好用二分法查找了,当然,前提是要把给出的序列从小到大或者从大到小排序。
代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
bool BinarySearch(int tmp[], int left, int right, int x){
int mid;
while(left<=right){
mid = (left+right)/2;
if(tmp[mid]==x) return true;//找到了,返回true
else if(tmp[mid]>x) right = mid-1;
else left = mid+1;
}
return false;//没找到,返回false
}
int a[101]={0};//存储输入的所有数字
int b[101]={0};//存储要查找的数字
int main(){
int n;
while(scanf("%d", &n) != EOF){
for(int i=0;i<n;i++) scanf("%d", &a[i]);
int m;
scanf("%d", &m);
for(int i=0;i<m;i++) scanf("%d", &b[i]);
sort(a, a+n);//对a数组从小到大排序
for(int i=0;i<m;i++){
if(BinarySearch(a, 0, n-1, b[i])) printf("YES\n");
else printf("NO\n");
}
}
return 0;
}
问题 D: 习题5-15 二分法求方程的根
题目不可用!!(黑人问号.jpg)
小结
这部分的题目还是比较简单的,主要都是查找类的问题,或者数学类问题(书上的二分法求根、二分法求根号的近似值、装水问题、木棒切割问题等等)。
对于查找类的题目,如果对数据规模比较小的其实用for循环遍历查找也没太大问题,但是如果数据规模比较大的话就最好用二分查找来做了,只要记得在二分查找之前要先将序列排序就行(单增单减都可以,但是对应的二分查找函数不一样)。
对于数学类的题目,我认为最重要的地方在于从题目中提取信息,找到两个变量之间可能形成的单增或者单减的关系,一旦找到这样的关系之后,接下来用二分法就非常好做了。