给定一个数字N和一个整数数组(所有nos小于2 ^ 15)。 (A是阵列100000的大小)
查找N的最大XOR值和数组中的整数。
Q不是查询(50000)并且是start,stop是数组中的范围。
输入:
A Q
a1 a2 a3 ...
N开始停止
输出:
N的最大XOR值和指定范围的数组中的整数。
例如:输入
15 2(2不是查询)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
10 6 10(查询1)
10 6 10(查询2)
输出:
13个
13
代码:
for(int i=start-1;i<stop;i++){
int t =no[i]^a;
if(maxxor<t)
maxxor=t;
}
cout << maxxor <<endl;
我需要比这快10-100倍的算法。排序太贵了。我也尝试了二进制树,位操作。
2x - 3x改善怎么样?这是可能的优化。
答案 0 :(得分:5)
可以开发更快的算法。
让我们调用N的位:a [0],a [1],...,a [15],例如,如果N = 13 = 0000000 00001101(二进制),则a [0] = a [1] ] = ... a [11] = 0,a [12] = 1,a [13] = 1,a [14] = 0,a [15] = 1.
算法的主要思想如下:如果[0] == 1,则最佳可能的答案将此位置零。如果[0] == 0,则最佳答案在此位置有一个答案。 所以首先你要检查你是否有一个带有所需位的数字。如果是,您应该只使用此位数。如果不是,你就把它反过来。 然后以相同的方式处理其他位。例如。如果a [0] == 1,a [1] == 0,首先检查是否有以零开头的数字,如果是,则检查是否有以01开头的数字。如果没有从零开始,那么你检查是否有一个数字与11开始等等...
所以你需要一个快速的算法来回答以下查询:是否有一个以位开头的数字......在范围开始,停止?
一种可能性:从数字的二进制表示构造trie。在每个节点中存储此前缀在数组中的所有位置(并对它们进行排序)。然后回答这个问题可以简单地通过这个trie。要检查start,stop范围中是否有合适的前缀,您应该对节点中存储的数组进行二进制搜索。
这可能导致复杂度为O(lg ^ 2 N)的算法更快。
这是代码,它没有经过多少测试,可能包含错误:
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
class TrieNode {
public:
TrieNode* next[2];
vector<int> positions;
TrieNode() {
next[0] = next[1] = NULL;
}
bool HasNumberInRange(int start, int stop) {
vector<int>::iterator it = lower_bound(
positions.begin(), positions.end(), start);
if (it == positions.end()) return false;
return *it < stop;
}
};
void AddNumberToTrie(int number, int index, TrieNode* base) {
TrieNode* cur = base;
// Go through all binary digits from most significant
for (int i = 14; i >= 0; i--) {
int digit = 0;
if ((number & (1 << i)) != 0) digit = 1;
cur->positions.push_back(index);
if (cur->next[digit] == NULL) {
cur->next[digit] = new TrieNode;
}
cur = cur->next[digit];
}
cur->positions.push_back(index);
}
int FindBestNumber(int a, int start, int stop, TrieNode* base) {
int best_num = 0;
TrieNode* cur = base;
for (int i = 14; i >= 0; i--) {
int digit = 1;
if ((a & (1 << i)) != 0) digit = 0;
if (cur->next[digit] == NULL ||
!cur->next[digit]->HasNumberInRange(start, stop))
digit = 1 - digit;
best_num *= 2;
best_num += digit;
cur = cur->next[digit];
}
return best_num;
}
int main() {
int n; scanf("%d", &n);
int q; scanf("%d", &q);
TrieNode base;
for (int i = 0; i < n; i++) {
int x; scanf("%d", &x);
AddNumberToTrie(x, i, &base);
}
for (int i = 0; i < q; i++) {
int a, start, stop;
// Finds biggest i, such that start <= i < stop and XOR with a is as big as possible
// Base index is 0
scanf("%d %d %d", &a, &start, &stop);
printf("%d\n", FindBestNumber(a, start, stop, &base)^a);
}
}
答案 1 :(得分:1)
如果您有多个具有相同范围的查询,则可以构建一个包含该范围内数字的树,如下所示:
使用深度为15的二叉树,其中数字位于叶子处,数字对应于通向它的路径(左边是0,右边是1)。
e.g。为0 1 4 7:
/ \
/ /\
/ \ / \
0 1 4 7
然后你的查询是N = n_1 n_2 n_3 ... n_15其中n_1是N的第一位,n_2是第二位...... 从根到叶子,如果n_i = 0(其中i是当前节点的深度),则必须做出选择,然后转到右边,否则转到左边。当你在叶子上时,它是最大的叶子。
一个查询的原始答案:
您的算法是最佳的,您需要检查数组中的所有数字。
使用编程技巧可能有一种方法可以让程序略快一些,但它与算法没有关联。
答案 2 :(得分:1)
您的算法以线性时间(O(start-stop)
或O(N)
为整个范围运行)。如果你不能假设输入数组已经有一个特殊的顺序,你可能无法更快地得到它。
您只能尝试优化循环中的开销,但这肯定不会显着提高速度。
编辑:
因为看起来你必须多次搜索同一个列表,但是使用不同的开始和结束索引。
这意味着对数组进行预排序也是不可能的,因为这会改变元素的顺序。 start
和end
毫无意义。
如果一个查询完全包含已扫描的范围,您可以尝试避免两次处理相同的范围。
或者尝试在通过数组迭代时同时考虑所有查询。
答案 3 :(得分:1)
我想出一个需要O(AlogM)时间和空间进行预处理的解决方案。每个查询都有O(log 2 M)时间。 M是整数的范围,在这个问题上是2 ^ 15。
对于
1st..Nth number,(Tree Group 1)
第1个(第A / 2)个数,(A / 2)th。第一个数,(树组2)
第1 ..(A / 4)个数,(A / 4)th ...(第A / 2)个数,(A / 2)th ..(3A / 4)th,(3A / 3)th .. Ath,(树组3)
.......,(树组4)
.......,
.......,(树组logA)
构造范围内所有数字的二进制表示的二进制trie。会有2M树。但是聚集的所有树木都不会超过O(AlogM)元素。对于包含x数字的树,树中最多只能有logM * x节点。并且每个数字仅包含在每个树组中的一个树中。
对于每个查询,您可以将范围拆分为我们已处理到树中的多个范围(不超过2logA)。对于每棵树,我们可以在O(logM)时间内找到最大XOR值(稍后将解释)。那是O(logA * logM)时间。
如何在树中找到最大值?如果当前数字在N中为0,则更喜欢1个孩子,否则更喜欢0个孩子。如果首选孩子存在,则继续该孩子,否则继续为另一个孩子。
答案 4 :(得分:0)
是的,或者你可以计算它,而不是浪费时间思考如何做得更好。
int maxXor(int l, int r) {
int highest_xor = 0;
int base = l;
int tbase = l;
int val = 0;
int variance = 0;
do
{
while(tbase + variance <= r)
{
val = base ^ tbase + variance;
if(val > highest_xor)
{
highest_xor = val;
}
variance += 1;
}
base +=1;
variance = 0;
}while(base <= r);
return highest_xor;
}