前言
一、题目
输出和为s的数组元素
Description
输入一个递增排序的数组和数字s
在数组中查找两个数,使得他们的和恰好为s。
如果有多对数字的和等于s,输出两个数的乘积最小的
Input
输入第一个数 n 表示数组长度
输入第二个数 s 表示两个数之和
其余数组中的数按空格分开输入
Output
输出满足条件的两个元素
二、解题思路
这道题如果暴力一点怎么做都好,但是如果想要又快又好,那就得花点心思。
注意一点,该数组是递增的。本题需要一个数学知识,两个数的差值越小,则两个数的乘积越大
证明如下:
假设固定两数的平均值 m,设 d 为两数与平均值的差,则 a = m + d, b = m - d
ab = (m + d)(m - d) = m^2 - d^2
可见 d 越小(即,两数离平均值越近,也就是两数差值越小),乘积越大
(不考虑数组中的数没有组合等于输入的数的情况)
所以为了让我们输出的两个数乘积越小,这两个数就越需要靠向两侧,而不是中间。所以应该从两侧开始搜索。
我们可以定义一个i变量指向数组头部,j变量指向数组尾部,如果最开始arry[i]+arry[j]>s(我们输入的数)那么说明这两个数的和偏大,需要j–,这个时候i不能++也不能–,因为–会使得arry越界,而++会使得arry[i]+arry[j]更大于s。当最开始的时候arry[i]+arry[j]<s时,需要i++,j不能++也不能–因为j++数组越界,j–会使得arry[i]+arry[j]更小于s。
一般情况下如果arry[i]+arry[j]>s那么会使得j–,那么下一次如果是arry[i]+arry[j]>s那么还是j–,若为arry[i]+arry[j]<s则应为i–,不会是j++因为这样就回到上一个状态了,避免绕圈,所以两个条件只需要做i++或j–。
代码如下:
#include<stdio.h>
int main() {
int arry[100] = { 0 };
int n = 0;
int s = 0;
scanf("%d %d", &n, &s);
for (int i = 0; i < n; i++) {
scanf("%d", &arry[i]);
}
int i = 0;
int j = n - 1;
while (i < j) {
if (arry[i] + arry[j] > s) {
j--;
}
else if (arry[i] + arry[j] < s) {
i++;
}
else {
printf("%d %d", arry[i], arry[j]);
break;
}
}
return 0;
}