OJ#194 C语言
题目连接:OJ - Online Judge (haizeix.com)
题目描述
给出一个有 n 个元素的数列 a 和一个整数 s ,其中数列 a 的元素是按照升序排列的。 请你在数列中找出两个元素 x,y,使得 x+y=s。
输入
输入第一行一个整数 n(1≤n≤105),表示数列中的元素个数。 接下来一个输入 n 个空格隔开的整数,表示输入的数列 a,保证是升序排列,并且−10^9≤ai≤10^9。 接下来一行输入一个整数 s(−10^9≤s≤10^9)。
输出
如果能找到满足条件的两个数,输出 "Yes" ,否者输出 "No" 。
样例输入1
5
1 2 3 4 5
4
样例输出1
Yes
样例输入2
5
1 2 4 4 5
4
样例输出2
No
数据规模与限定
时间限制:1 s
内存限制:64 M
本人为2020级大一新生,学习C语言时间不到两学期,代码肯定不是最优的,仅提供一种解决思路,欢迎大家讨论学习。
思路:
因为给的元素是升序并且数值较大,所以必定用到二分法(binary search),同时为了更加节约时间,我们需要进一步缩小二分的范围.
对于一组升序元素来所说,该组元素所能组成的最大值为最后两个元素的和。如1 2 3 4 5这组元素所能组成的最大数为4+5=9,故大于9的s可以直接判定为No;
同时s=x+y;x和y必定满足x<=s/2,y>=s/2。
s=8
x y
0 8
1 7
2 6
3 5
4 4
因此binary-search的target即为s-x,判定范围是x往后的元素到s/2(并不需要找出s/2的具体位置,因为元素中可能并没有s/2,故只需要比较当前x是否小于s/2)
元素1 2 3 4 5 5 6 7
s=7
判断:x的取值为x<s/2
x=1
二分判定范围为2 3 4 5 5 6 7
若找到s-x=6,循环退出,输出Yes。
若没有找到s-x=6,取下一地址的内容。
x=2
二分判定范围为3 4 5 5 6 7
若是当前指针指向的内容小于s/2,下一指针指向的内容大于s/2,并且当前判定依然不是Yes的话,则输出No
代码如下:
#include<stdlib.h>
#include<stdio.h>
#define ok 1
#define error 0
int binary_search(int* p, int head, int tail, int target);//二分查找
int main()
{
int n, s, * a,*head,*tail;
scanf("%d", &n);
a = (int*)malloc(sizeof(int) * n);//申请内存
head = a;//创建头指针
tail = a + n - 1;//创建尾指针
for (int i = 0; i < n; i++)//输入数据
{
scanf("%d", a + i);
}
scanf("%d", &s);
if (*tail + *(tail - 1) >= s)//判断当前元素是否可以组成s
for (int m = n; *a <= s / 2 && a < tail; a++, m--)
{
if (binary_search(a + 1, 0, m - 2, s - *a))//a+1是要二分的元素的首地址,0是头指针,m-2是被判断的元素的长度同时也是尾指针,s-*a是target
{
printf("Yes");
break;
}
else if (*(a+1) > s/2)
{
printf("No");
break;
}
}
else
printf("No");
a = head;
free(a);
return 0;
}
int binary_search(int* p, int head, int tail, int target)
{
while (head <= tail)
{
int mid = (head + tail) >> 1;//等价于mid = (head + tail) / 2
if (*(p + mid) > target)
{
tail=mid-1;
}
else if (*(p + mid) < target)
{
head=mid+1;
}
if (*(p + mid) == target)
return ok;
}
return error;
}
代码优化思路,二分的判定范围为s/2到s本身,但暂时未想出如何找到这两个值,并且相较于寻找这两个值所用的时间不知道是否可以减少总的运算时间。