Leetcode周赛 1521. Find a Value of a Mysterious Function Closest to Target

吹雪❣俺は嫁た。いつもいつも大好き


a b s ( a r r [ l ] & a r r [ l + 1 ] & a r r [ r ] t a r g e t ) abs( arr[l] \& arr[l+1] \cdots \&arr[r] - target)
最小值


先贴一个思路很棒的优化,充分利用单调不增的性质
AND(l,r)表示 a r r [ l ] & a r r [ l + 1 ] & a r r [ r ] arr[l] \& arr[l+1] \cdots \& arr[r] ,根据&运算规则我们知道 a < a&b,因此可推得
A N D ( l , r ) A N D ( l , r + 1 ) A N D ( l , r ) t a r g e t A N D ( l , r + 1 ) t a r g e t (1) AND(l,r)\geq AND(l,r+1) \\ AND(l,r) - target \geq AND(l,r+1) - target \tag 1
所以当AND(l,r)<=t时就可以提前退出了
此外,考虑另一种情况:AND递减的最小值仍大于t。这说明从某一位k开始,后面的数在某些位上都是1,且AND(k,len-1)>=t,那么k之后,无论怎么组合,结果都不会比AND(k,len-1)更小,所以也可以提前退出,确定答案。
以上是Leetcode讨论区中给出的方法,但是这个方法仍不够完美,因为其对于某些特殊情况仍会恶化至O(n2)。我们来考虑如下数据:

target = 2
arr = {5,5,5,5,5......1}

arr长为1e5,除了最后最后一位是1,其他都是5。
按之前给出的流程,存在sum<=t的情况,但是发生在数组末尾,所以这时程序性能急剧恶化,退化到了O(n2)的情况。为了应对这种情况,可以考虑在遍历时跳过相等且连续的元素,这样不仅不影响正确性且避免了重复的计算,详见代码

class Solution {
    public int closestToTarget(int[] arr, int t) {
        int ans = Math.abs(arr[0] - t);
        int len = arr.length;
        for(int l=0;l<len;l++){
            int sum = arr[l];
            for(int r=l;r<len;r++){
                sum = sum & arr[r];
                ans = Math.min(ans,Math.abs(sum - t));
                if(sum <= t) break;
            }
            if(sum >= t) break;
            int pos = l+1;
            while(pos<len && arr[pos]==arr[l])
                pos++;
            l = pos - 1;
        }
        return ans;
    }
}

其实这样好像也不对,因为这个递减的性质有个默认的设定,就是
( l , r ) , A N D ( l , r ) > = t \forall(l,r),AND(l,r)>=t
任意子区间AND运算后结果都要大于t。若
A N D ( l , r ) t A N D ( l , r + 1 ) A N D ( l , r ) t 0 A N D ( l , r + 1 ) AND(l,r)\geq t\geq AND(l,r+1) \\ AND(l,r) - t \geq 0 \geq AND(l,r+1)
取绝对值后,若仍保持递减性质则需
A N D ( l , r ) + A N D ( l , r + 1 ) 2 t AND(l,r) + AND(l,r+1) \leq 2t
但是显然,这样的性质是难以保证的


附讨论区另一题解
ネコではないです

仍然根据&运算的性质,对于arr[i]来说,后续的状态最多之后32种。因为&的过程就是将1flip为0的过程,所以最多每次翻一位,也就是32次。所以借助set集和不重复的特性记录不同的状态,然后移动到新的位置更新和添加状态就能遍历所有子区间的结果

class Solution {
    public int closestToTarget(int[] arr, int t) {
        int ans = Math.abs(arr[0] - t);
        int len = arr.length;
        HashSet<Integer> now = new HashSet();
        for(int l=0;l<len;l++){
            HashSet<Integer> tmp = new HashSet();
            for(Integer ele : now){
                tmp.add(ele & arr[l]);
            }
            tmp.add(arr[l]);
            for(Integer ele : tmp){
                ans = Math.min(ans,Math.abs(ele - t));
            }
            now = tmp;
        }
        return ans;
    }
}

猜你喜欢

转载自blog.csdn.net/white_156/article/details/107450525