Maximise the AND
Maximise the AND
给定一个n个非负整数数组:A1, A2,…,an。如何找到一对整数Au, Av(1≤u <v≤N)使得(Au和Av)尽可能大。>
示例:设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];
}
}
}
加快速度的一种方法是利用这样一个事实,即如果任意两个数字中设置了任何高位,那么这两个数字的与将始终大于使用低位的任何组合。
因此,如果你按位数排序,你可能会大大减少操作的次数。
为了有效地找到最高有效位,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;
}
该算法的最坏情况运行时间为0 (N*N),但平均而言它的性能应该要好得多。您还可以通过在搜索较小的向量时重复分区步骤来进一步提高性能(只需记住忽略分区步骤中最重要的位,这样做会将性能提高到最坏的情况O(N))。
保证输入数据中没有重复将进一步提高性能。
- 按降序排序数组。
- 取前两个数字。如果它们都在2的两个连续幂之间(比如2^k和2^(k+1)),那么你可以删除所有小于2^k的元素。
- 从剩余的元素中减去2^k。
- 重复步骤2和3,直到数组中的元素数为2。
注意:如果你发现只有最大的元素在2^k和2^(k+1)之间,并且第二大元素小于2^k,那么你不删除任何元素,只从最大的元素中减去2^k。
还可以确定元素在系列{1,2,4,8,16,…}可以在O(log(log(MAX)))时间内完成,其中MAX是数组中最大的数字。
我没有测试这个,我也不打算测试。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;
}
我又遇到了这个问题,并以一种不同的方式解决了它(对我来说更容易理解):
unsigned findMaxAnd(vector<unsigned> &input) {
vector<unsigned> candidates;
for(unsigned mask = 1<<31; mask; mask >>= 1) {
for(unsigned i : input)
if(i&mask)
candidates.push_back(i);
if (candidates.size() >= 2)
input = move(candidates);
candidates = vector<unsigned>();
}
if(input.size() < 2) {
return 0;
return input[0]&input[1];
}
这是一个O(N * log MAX_A)的解决方案:
1)让我们贪婪地构造答案,从最高位到最低位迭代。
2)为了做到这一点,我们可以维持一个S个当前适合的数的集合。最初,它由数组中的所有数字组成。我们还假设最初ANS = 0。
3)现在让我们从最高到最低遍历所有的位。假设当前位是b
4)如果S中第B位值为1的元素数大于1,则在不改变ANS中更高位的值的情况下,有可能在这个位置上有1,因此我们应该在ANS中添加2^B,并从S中删除所有该位值为0的元素(它们不再适合)。
5)否则在这个位置不可能得到1,所以我们不改变S和ANS,继续下一位
- 这在C++ "It does not own the underlying data, and so is cheap to copy or assign"中意味着什么
- 为什么'allocate in one library and free in the other'是错误的
- Adding MAKEFLAGS from MPC(The Makefile, Project, and Workspa
- Visual C++ and the .NET Framework?
- std::stringstream and the str method
- QSortFilterProxyModel and the slot setFilterFixedString
- 预处理器:"The definition also permits you to split an identifier at any position and get exactly two tok
- PCAP Destination and Source are the same
- C++ Modules and the C++ ABI
- The differences between C++03 and C++14
- DirectShow and the Canon EOS 5D Mark II
- Maximise the AND
- Tegra Nsight and locating the Android NDK
- boost::ptree and the KeyCompare function?
- What is the differenece between Shape s = new Circle(); and
- Android and the NDK/librarys
- 为什么"dumb to override the & operator and return *this*"?
- [conv]/6 中"The expression e is used as a glvalue if and only if the initialization uses it as a glva
- The SECURITY_ATTRIBUTES struct and CreateNamedPipe()
- XLW - 编译的 XLL "The file format and extension don't match"