問題描述:
給定三個數組A,B,C,從這三個數組中分別取一個元素a,b,c,使得|a-b|+|b-c|+|c-a| 最小。
【方案一】
<直接枚舉法>
該題最直接的方法是枚舉,即枚舉三個數組組成的所有三元組<a,b,c>,然後求出|a-b|+|b-c|+|c-a| 最小值。
【方案二】
<巧妙轉化法>
一般而言,見到絕對值號,首先想到的應是去絕對值號。
枚舉6種a,b,c之間的大小關係,如a>=b>=c,則|a-b|+|b-c|+|c-a| =2(a-c)。
可以得到結論:|a-b|+|b-c|+|c-a|只與a,b,c中最大值和最小值有關。這個很容易通過畫數軸證明,
在數軸上,三個點兩兩距離之和記爲最大值與最小值距離的兩倍。
得到上面結論後,怎樣高效的得到|a-b|+|b-c|+|c-a| 最小值呢?
想一下極端情況:將三個數組合併成一個數組,對該數組排序後,如果相鄰三個數正好來自三個不同數組,
則|a-b|+|b-c|+|c-a| 最小值肯定可通過相鄰三個數獲得。這樣,僅掃描一遍便可求出該最小值。
將該極端情況的算法擴展到一般情況的算法:先對三個數組進行排序,然後用三個指針分別指向三個數組起始位置,
獲取三個指針指向的元素中最大值max和最小值min,計算2(max-min),之後將最小元素對應的指針前移一位,
重複以上過程,保留2(max-min)的最小值即可。
void print_triple_with_min_dist(int *a, int *b, int *c, int n, int m, int s)
{
sort(a, a + n);
sort(b, b + m);
sort(c, c + s);
int p = 0, q = 0, r = 0, minDis = 1 << 15;
while(p < n && q < m && r < s)
{
int maxP, minP;
maxP = max(max(a[p], b[q]), c[r]);
minP = min(min(a[p], b[q]), c[r]);
minDis = min(minDis, maxP - minP);
if(a[p] == minP)
p ++;
else if(b[q] == minP)
q ++;
else
r ++;
}
cout << minDis << endl;
}