ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2020-02-22 09:06:17  阅读:496  来源: 互联网

标签:x1 题中 int y1 abs 绝对值 x2 y2 最值


引言

现在算法题中,有时会遇到求解绝对值最值的问题,比如给定一个数组,求解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值最大能为多少?

思路:

  • 如果我们选择不翻转子数组,那么它的valuevaluevalue值可以通过O(n)O(n)O(n)时间遍历得到。这个值我们记录为sum1sum_1sum1​。
  • 如果我们翻转子数组[L,R][L, R][L,R],这时新数组的valuevaluevalue为:
    sum1+a[R]a[L1]+a[L]a[R+1]a[L]a[L1]a[R]a[R+1]sum_1 + |a[R] - a[L-1]| + |a[L] - a[R+1]| - |a[L] - a[L-1]| - |a[R] - a[R+1]|sum1​+∣a[R]−a[L−1]∣+∣a[L]−a[R+1]∣−∣a[L]−a[L−1]∣−∣a[R]−a[R+1]∣,这里sum1sum_1sum1​是不变的就可以不管它,然后又出现了绝对值最值问题。
    此时我们只展开前两个绝对值,因为展开绝对值的目的是把与LLL相关的放一起,与RRR相关的放一起,而后两个绝对值里只包含L/RL/RL/R了。
    展开后,得到这二者:
    1.(a[R]a[R+1]a[R]a[R+1])(a[L1]a[L]+a[L]a[L1])(a[R]-a[R+1] - |a[R] - a[R+1]|) - (a[L-1] - a[L] + |a[L] - a[L-1]|)(a[R]−a[R+1]−∣a[R]−a[R+1]∣)−(a[L−1]−a[L]+∣a[L]−a[L−1]∣)
    2.(a[R]+a[R+1]a[R]a[R+1])(a[L1]+a[L]+a[L]a[L1])(a[R]+a[R+1] - |a[R] - a[R+1]|) - (a[L-1] + a[L] + |a[L] - a[L-1]|)(a[R]+a[R+1]−∣a[R]−a[R+1]∣)−(a[L−1]+a[L]+∣a[L]−a[L−1]∣)
    最后,取二者最大值即可。

代码:

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有