最大化AND

时间:2014-07-27 06:03:30

标签: c++ algorithm

给定n个非负整数的数组:A1,A2,...,AN。如何找到一对整数Au,Av(1≤u

示例:设N = 4,数组为[2 4 8 10]。这里的答案是8

解释

2 and 4 = 0
2 and 8 = 0
2 and 10 = 2
4 and 8 = 0
4 and 10 = 0
8 and 10 = 8

如果N可以达到10 ^ 5,该如何做到这一点。 我有O(N ^ 2)解决方案。但效率不高

代码:

for(int i=0;i<n;i++){
    for(int j=i+1;j<n;j++){
        if(arr[i] & arr[j] > ans)
        {
            ans=arr[i] & arr[j];
        }
    }
}

4 个答案:

答案 0 :(得分:4)

你可以加速它的一种方法是利用这样一个事实,即如果任何两个数字中的任何一个高位被设置,那么这两个数字的AND总是大于使用低位的任何组合。 / p>

因此,如果您按照设置的顺序对数字进行排序,则可能会大幅减少操作次数。

为了有效地找到最重要的位,GCC有一个内置的内在函数:__builtin_clz(unsigned int x),它返回最重要的设置位的索引。 (其他编译器具有类似的内在函数,至少转换为x86上的单个指令。)

const unsigned int BITS = sizeof(unsigned int)*8; // Assuming 8 bit bytes.

// Your implementation over.
unsigned int max_and_trivial( const std::vector<unsigned int> & input);    

// Partition the set.
unsigned int max_and( const std::vector<unsigned int> & input ) {
    // For small input, just use the trivial algorithm.
    if ( input.size() < 100 ) { 
        return max_and_trivial(input);
    }        

    std::vector<unsigned int> by_bit[BITS];

    for ( auto elem : input ) {
         unsigned int mask = elem;
         while (mask) { // Ignore elements that are 0.
             unsigned int most_sig = __builtin_clz(mask);
             by_bits[ most_sig ].push_back(elem);
             mask ^= (0x1 << BITS-1) >>  most_sig;
         }
    }

    // Now, if any of the vectors in by_bits have more 
    // than one element, the one with the highest index 
    // will include the largest AND-value.

    for ( unsigned int i = BITS-1; i >= 0; i--) {
        if ( by_bits[i].size() > 1 ) {
             return max_and_trivial( by_bits[i]);
        }
    }

    // If you get here, the largest value is 0.
    return 0;
}

此算法仍然具有最差情况运行时O(N * N),但平均来说它应该表现得更好。当您搜索较小的向量时,您还可以通过重复分区步骤来进一步提高性能(只需记住忽略分区步骤中的最高位,这样做可以将性能提高到最坏的O(N)情况)。

保证输入数据中没有重复项将进一步提高性能。

答案 1 :(得分:3)

  1. 按降序对数组进行排序。
  2. 取前两个数字。如果它们都在两个连续2的幂之间(比如2 ^ k和2 ^(k + 1)),那么你可以删除所有小于2 ^ k的元素。
  3. 从剩余的元素中减去2 ^ k。
  4. 重复步骤2和3,直到数组中的元素数为2.
  5. 注意:如果您发现只有最大元素介于2 ^ k和2 ^(k + 1)之间且第二大元素小于2 ^ k,那么您将不会删除任何元素,而只是减去2 ^ k来自最大元素。

    此外,确定元素在系列{1,2,4,8,16,...}中的位置可以在O(log(log(MAX)))时间内完成,其中MAX是最大数字数组。

答案 2 :(得分:3)

我没有测试过这个,我不会去。 O(N)存储器和O(N)复杂度。

#include <vector>
#include <utility>
#include <algorithm>

using namespace std;


/*
 * The idea is as follows:
 * 1.) Create a mathematical set A that holds integers.
 * 2.) Initialize importantBit = highest bit in any integer in v
 * 3.) Put into A all integers that have importantBit set to 1.
 * 4.) If |A| = 2, that is our answer. If |A| < 2, --importantBit and try again. If |A| > 2, basically
 *     redo the problem but only on the integers in set A.
 *
 * Keep "set A" at the beginning of v.
 */
pair<unsigned, unsigned> find_and_sum_pair(vector<unsigned> v)
{
    // Find highest bit in v.
    int importantBit = 0;
    for(auto num : v)
        importantBit = max(importantBit, highest_bit_index(num));

    // Move all elements with imortantBit to front of vector until doing so gives us at least 2 in the set.
    int setEnd;
    while((setEnd = partial_sort_for_bit(v, importantBit, v.size())) < 2 && importantBit > 0)
        --importantBit;

    // If the set is never sufficient, no answer exists
    if(importantBit == 0)
        return pair<unsigned, unsigned>();

    // Repeat the problem only on the subset defined by A until |A| = 2 and impBit > 0 or impBit  = 0
    while(importantBit > 1)
    {
        unsigned secondSetEnd = partial_sort_for_bit(v, --importantBit, setEnd);
        if(secondSetEnd >= 2)
            setEnd = secondSetEnd;
    }

    return pair<unsigned, unsigned>(v[0], v[1]);
}

// Returns end index (1 past last) of set A
int partial_sort_for_bit(vector<unsigned> &v, unsigned importantBit, unsigned vSize)
{
    unsigned setEnd = 0;

    unsigned mask = 1<<(importantBit-1);
    for(decltype(v.size()) index = 0; index < vSize; ++index)
        if(v[index]&mask > 0)
            swap(v[index], v[setEnd++]);

    return setEnd;
}


unsigned highest_bit_index(unsigned i)
{
    unsigned ret = i != 0;
    while(i >>= 1)
        ++ret;
    return ret;
}

答案 3 :(得分:0)

这是一个O(N * log MAX_A)解决方案:

1)让我们贪婪地构建答案,从最高位迭代到最低位。

2)要做到这一点,人们可以保留当前适合的一组数字。最初,它由数组中的所有数字组成。我们还假设最初ANS = 0。

3)现在让我们迭代从最高到最低的所有位。让我们说当前位是B.

4)如果S中第1位值为1的元素数大于1,则可能在此位置有1而不改变ANS中高位的值,所以我们应该加2 ^ B到ANS并删除S中具有0位值的所有元素(它们不再适合)。

5)否则,在此位置无法获得1,因此我们不会更改S和ANS并继续下一位。