编程语言
首页 > 编程语言> > 算法题中求解绝对值最值的技巧

算法题中求解绝对值最值的技巧

作者:互联网

引言

现在算法题中,有时会遇到求解绝对值最值的问题,比如给定一个数组,求解absaiajabs|a_i - a_j|abs∣ai​−aj​∣的最大值。诸如此类问题,暴力解法是用O(n2)O(n^2)O(n2)时间复杂度遍历i,ji,ji,j。而一种常用优化是将绝对值拆开,比如absaiaj=max(aiaj,ajai)abs|a_i - a_j| = max(a_i - a_j, a_j - a_i)abs∣ai​−aj​∣=max(ai​−aj​,aj​−ai​),将问题拆成两个子问题,而每个子问题通过维护当前最小值,即O(n)O(n)O(n)的时间可解出。最后通过O(1)O(1)O(1)取二者最值即可。

下面分享三道利用此技巧的算法题,难度按升序排列。

Leetcode 1131

题意:
给定两个数组arr1arr1arr1和arr2arr2arr2,让找两个下标iii和jjj,使得arr1[i]arr1[j]+arr2[i]arr2[j]+ij|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|∣arr1[i]−arr1[j]∣+∣arr2[i]−arr2[j]∣+∣i−j∣最大。

思路:
这里直接拆开三个绝对值,得到以下8个子问题:
本图来源于该题目的解答
然后我们把有关于下标iii和下标jjj的项提出来放在一起,发现其实子问题只有以下四个:1&8;2&7;3&6;4&5(比如1和8,它们只不过把两个下标iii和jjj顺序调换了下)。
而这四个子问题的差别就是它们中的符号为加减法的排列组合:++++++; ++ -+−; +- +−+; - -−−。这样通过四个O(n)O(n)O(n)的循环即可求出最优解。

代码:

const int INF = 0x3f3f3f3f;
class Solution {
public:
    int maxAbsValExpr(vector<int>& x, vector<int>& y) {
        int res = 0, n = x.size();
        for (int sign1=-1; sign1<=1; sign1+=2)
            for (int sign2=-1; sign2<=1; sign2+=2) {
                int mx = -INF, mn = INF;
                for (int i=0; i<n; i++) {
                    int val = x[i] + sign1*y[i] + sign2*i;
                    mx = max(mx, val);
                    mn = min(mn, val);
                }
                res = max(res, mx - mn);
            }
        return res;
    }
};

Leetcode 1330

题意:
给定一个数组numsnumsnums,它的valuevaluevalue值被定义为所有nums[i]nums[i+1]|nums[i]-nums[i+1]|∣nums[i]−nums[i+1]∣的和(其中0<=i<=n0 <= i <= n0<=i<=n)。我们有一个操作,能使它某一段连续的子数组翻转一次,问翻转前/后这个数组的valuevaluevalue值最大能为多少?

思路:

代码:

class Solution {
public:
    
    int solve(const vector<int>& A) {
        int n = A.size(), base = 0;
        for (int i=0; i<n-1; i++)
            base += abs(A[i] - A[i+1]);
        int inc = 0;
        for (int sign=-1; sign<=1; sign+=2) {
            int mnVal = A[0] + sign*A[1] + abs(A[0]-A[1]);
            for (int i=1; i<n-1; i++) {
                int biggerVal = A[i] + sign*A[i+1] - abs(A[i]-A[i+1]);
                inc = max(inc, biggerVal - mnVal);
                mnVal = min(mnVal, A[i] + sign*A[i+1] + abs(A[i]-A[i+1]));
            }
        }
        return base + inc;
    }
    
    int maxValueAfterReverse(vector<int>& nums) {
        if (nums.size() <= 1) return 0;
        int res1 = solve(nums);
        reverse(nums.begin(), nums.end());
        int res2 = solve(nums);
        return max(res1, res2);
    }
};

Google Kick Start Round A 2019 - Parcels

题意:
给定一个RCR*CR∗C的01地图,标记为1的位置为快递站点,标记为0的位置为收快递的区域。假如我们站在某个0位置,那么送快递的时间为距离我最近的快递站到我当前位置的曼哈顿距离。为了减小送快递时间,我们可以在某个0的位置新建一个快递站点,问建立完后大家收快递所需要的最长时间最快是多少?

思路:
这个题暴力解法是,对于每个潜在位置,都试图建立一个快递站,然后BFS求一遍最长时间,时间复杂度是O((RC)2)O((RC)^2)O((RC)2)。

由于新建快递站点的位置很难确定,我们可以先用二分法把最优化问题转化为判定性问题,即,给定一个最长时间TTT,我们能否找到新增一个新快递站,使得所有快递的运输时间都小于等于TTT?

那么我们二分枚举TTT的值,然后对于某个TTT,遍历一边整个地图找到所有运输时间大于TTT的位置,记录下来(假设有mmm个,m<RCm < RCm<RC)。现在问题存不存在一个新快递快递站点,使得这些位置的运输时间小于T。

这里曼哈顿距离是绝对值形式表示的,于是两点之间的距离可以被表示为:dist((x1,y1),(x2,y2))=max(abs(x1+y1(x2+y2)),abs(x1y1(x2y2)))dist((x1, y1), (x2, y2)) = max(abs(x1 + y1 - (x2 + y2)), abs(x1 - y1 - (x2 - y2)))dist((x1,y1),(x2,y2))=max(abs(x1+y1−(x2+y2)),abs(x1−y1−(x2−y2)))。这里假设(x1,y1)(x1, y1)(x1,y1)为快递点的位置,(x2,y2)(x2, y2)(x2,y2)为运输时间大于TTT的位置,我们遍可以利用O(m)O(m)O(m)的方式求出(x2+y2)(x2 + y2)(x2+y2)和(x2y2)(x2 - y2)(x2−y2)的最值,然后再遍历一边地图检验是否存在这一快递点(x1,y1)(x1, y1)(x1,y1)即可。

这种解法利用了二分和绝对值优化,它总时间复杂度为O(RClog(R+C))O(RC *log(R+C))O(RC∗log(R+C))。本题还有一个O(RC)O(RC)O(RC)的方法,在此不做介绍(不会…)。

关键代码:

bool ok(int K) {
    vector<pair<int, int> > focusBlanks;
    int mx1 = -INF, mn1 = INF, mx2 = -INF, mn2 = INF;
    for (int i=0; i<row; i++) {
        for (int j=0; j<col; j++) if (dis[i][j] > K) {
            focusBlanks.push_back(make_pair(i, j));
            mx1 = max(mx1, i + j); mn1 = min(mn1, i + j);
            mx2 = max(mx2, i - j); mn2 = min(mn2, i - j);
        }
    }
    if (focusBlanks.size() == 0) return true;
    for (int i=0; i<blanks.size(); i++) {
        int x2 = blanks[i].first, y2 = blanks[i].second;
        int dis1 = max(abs(mx1 - (x2 + y2)), abs(mn1 - (x2 + y2)));
        int dis2 = max(abs(mx2 - (x2 - y2)), abs(mn2 - (x2 - y2)));
        if (max(dis1, dis2) <= K) return true;
    }
    // printf("%d : %d\n", K, false);
    return false;
}
Site1997 发布了40 篇原创文章 · 获赞 44 · 访问量 9万+ 私信 关注

标签:x1,题中,int,y1,abs,绝对值,x2,y2,最值
来源: https://blog.csdn.net/Site1997/article/details/104177941