1. 程式人生 > >LeetCode 910. Smallest Range II

LeetCode 910. Smallest Range II

很有意思的一道數學推理題目, 剪枝以後解法也很簡潔。
初看貌似需要把每個數跟其他數作比較。但排序以後可以發現情況大大簡化:
對於任一對元素a[i] < a[j], a[i] - k和a[j] + k 的情況可以排除, 因為會產生比原值更大的差, 所以對於原有陣列的最小值min最大值max, (min - k, max + k)的情況可以排除。剩下的三種情況, (min - k, max - k), (min + k, max + k) 和 (min + k, max - k),後兩種等價於原值max - min, 我們可以把初始最大差值設為max - min, 確保最終結果不會比這個平凡值更壞。
對於最後一種情況(min + k, max - k), 需要繼續分情況討論。
方便起見,我們可以把所有元素都預先-k, 然後從最小元素開始,嘗試依次把各元素+2*k.
我們可以證明,如果選擇a[i] + 2 * k,那麼之前任一元素a[j] (0 <= j < i)加上 2 * k,都不會使結果更壞。

證明見此:
B = [A[0] + 2K, ...,A[i-1]+2K, A[i] + 0, A[i+1] +2K, ..., A[j]+2K, ..., A[j+1]+0, A[n-1]+0]
B' = [A[0] + 2K, ...,A[i-1]+2K, A[i] + 2K, A[i+1] +2K, ..., A[j]+2K, ..., A[j+1]+0, A[n-1]+0]
A[i]+2K is between A[i-1] + 2K and A[i+1] +2K, so it must stand in the range of B.
B' is not worse than B, it can be easily generalized to multiple elements added by 0 between the ones added by 2K.
於是當我們考慮a[i] + 2 * k時,可以假設之前的元素都已經加上了2 * k。
當前最大差值取決於:
1) 當前數列最小值:
可能為a[i+1]
a[i+1],...a[0]+2*k...a[n-1]...
或a[0]+2*k
a[0]+2*k..., a[i+1],...a[n-1]...
2) 以及當前數列最大值:
可能為a[i]+2*k
...a[n-1]...a[i]+2*k
或者a[n-1]
...a[i]+2*k...a[n-1]

因此我們只需遍歷所有元素,計算當前元素加上2*k的以後的陣列的最大差值,取其中的最小值即可。

public:
    int smallestRangeII(vector<int>& a, int k) {
        size_t len = a.size();
        if(len <= 1) return 0;
        sort(a.begin(), a.end());
        int front = a.front(), back = a.back(), d = back - front, start = front + 2 * k;
        if( k >= d) return d;
        
int ans = d; for(size_t i = 0; i < len - 1; i++){ int lo = min(start, a[i + 1]), hi = max(a[i] + 2 * k, back); ans = min(ans, hi - lo); } return ans; }

 參考:https://zhanghuimeng.github.io/post/leetcode-910-smallest-range-ii/