2020年统考真题
定义三元组$ (a,b,c)$ ( a,b,c 均为正数)的距离 D = ∣ a − b ∣ + ∣ b − c ∣ + ∣ c − a ∣ D=|a−b|+|b−c|+|c−a| D=∣a−b∣+∣b−c∣+∣c−a∣ 。给定 3个非空整数集合 S1 、 S2 和 S3 ,按升序分别存储在 3 个数组中。请设计一个尽可能高效的算法,让算并输出所有可能的三元组 (a,b,c) (其中 a ∈ S 1 , b ∈ S 2 , c ∈ S 3 a∈S1,b∈S2,c∈S3 a∈S1,b∈S2,c∈S3)中的最小距离。例如 S 1 = { − 1 , 0 , 9 } S1=\{−1,0,9\} S1={−1,0,9} , S 2 = { − 25 , − 10 , 10 , 11 } S2=\{−25,−10,10,11\} S2={−25,−10,10,11} , S 3 = { 2 , 9 , 17 , 30 , 41 } S3=\{2,9,17,30,41\} S3={2,9,17,30,41} ,则最小距离为 2 ,相应的三元组为 ( 9 , 10 , 9 ) (9,10,9) (9,10,9) 。要求:
- 给出算法的基本设计思想。
- 根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
- 说明你所设计算法的时间复杂度和空间复杂度。
设计思想
首先可以设想在一个数轴上,其有三个点分别为a,b,c(从小到大排序,如图)
那我们可以得到如下关系:
L
1
=
∣
a
−
b
∣
L
2
=
∣
c
−
b
∣
L
3
=
∣
c
−
a
∣
L
1
+
L
2
+
L
3
=
2
∣
c
−
a
∣
=
2
L
3
L_1 = |a-b|\\ L_2 = |c-b|\\ L_3 = |c-a|\\ L_1+L_2+L_3 = 2|c-a| = 2L_3
L1=∣a−b∣L2=∣c−b∣L3=∣c−a∣L1+L2+L3=2∣c−a∣=2L3
所以说,三元组中的距离只跟相距最大的两点有关!
所以说,我们想要获取到距离最短,那么改动b的位置是没有用的,甚至会让它距离更远(当b移动到 [ a , c ] [a,c] [a,c]外面以后)
所以我们只能移动a和c
但是由题目可以知道,三个数组中的数据都是按升序排序的,那么这就成为我们优化时间复杂度的一个突破口
都是升序排序。。。。对应到上面那张图就是:所有点只能从左向右移动!
那么在这一限制条件下,我们向右移动c肯定不是一个明智的想法,因为这只会让距离越来越大
那么b不能移动,c也不能移动,那我们只能移动a了,其实想一想,移动a确实可能能让距离更短
其实这道题我们可以想象一下,我们现在有一根橡皮筋,那么距离其实就是固定了橡皮筋a,c以后橡皮筋的长度,而我们要做的就是找出什么时候橡皮筋最松
好,继续说
由于我们上面所说,我们只能向右移动a,但是也有可能a移动太过了,比如超过了b,或者甚至超过了c,那么我们可以给它重新标个号,从左到右重新标为a,b,c
所以说到最后,其实这就变成了一道贪心题,我们不断尝试向右移动最小的那个点,看看能否让距离变短,不能就继续
那我们对比一下暴力求解的算法?
暴力求解时,我们会先固定i,j,然后一个个尝试k( i , j , k i,j,k i,j,k是数组A,B,C的下标, A [ i ] , B [ j ] , C [ k ] A[i],B[j],C[k] A[i],B[j],C[k]是上面所说的变换的a,b,c),但是我们会发现,无论怎么尝试,都只有当 A [ i ] ≤ C [ k ] ≤ B [ j ] A[i]\leq C[k]\leq B[j] A[i]≤C[k]≤B[j]时,才是当前状态(指当前固定好的i,j)的最小值,我们假设 A [ i ] ≤ C [ m . . . n ] ≤ B [ j ] A[i]\leq C[m...n]\leq B[j] A[i]≤C[m...n]≤B[j],那么m,n里面的比较是毫无意义的,因为我们知道它一定会是最小距离,而我们说了,我们是从左向右遍历序列的,所以我们很清楚,当k遍历到m时,再往后已经没有意义的,所以我们其实已经可以跳出循环了,当然,在固定i,k或者j,k时也是如此,所以我们不如使用三指针,让他们选一个最小的值,让其指针往右走,这才会造成真正我们需要的不同的状态,而不是那些越往后距离越大或者往后也没有变化的状态(这些状态是冗余的,没必要)
简而言之,只让最小的那个值往右走,这才会出现我们需要的尽可能小的状态!!!在这些状态中找最小值才是有效的!!!
最终的代码如下:
//p18 14
//求绝对值
#define MAX_INT 0x7fffffff;
int abs(int a){
return a > 0 ? a : -a;
}
//a是否为最小值
bool isMin(int a,int b,int c){
return a <= b && a <= c;
}
int findMinTrip(SqlList &L1,SqlList &L2,SqlList &L3){
ElemType minTrap = MAX_INT;
int i = 0,j = 0,k = 0;
int len1 = L1.length,len2 = L2.length,len3 = L3.length;
while (i<len1 && j < len2 && k<len3 && minTrap > 0)
{
int D = abs(L1.data[i] - L2.data[j]) + abs(L1.data[i] - L3.data[k]) + abs(L2.data[j] - L3.data[k]);
minTrap = minTrap < D ? minTrap : D;
if(isMin(L1.data[i],L2.data[j],L3.data[k])){
i++;
}
else if(isMin(L2.data[j],L1.data[i],L3.data[k])){
j++;
}
else if(isMin(L3.data[k],L1.data[i],L2.data[j])){
k++;
}
}
return minTrap;
}