题目描述
难度分:1714
输入n(1≤n≤150000)和长为n的数组a(0≤a[i]<230)。
选一个非负整数x,然后把每个a[i]更新成a[i]⊕x。
输出max(a)的最小值。
输入样例1
3
12 18 11
输出样例1
16
输入样例2
10
0 0 0 0 0 0 0 0 0 0
输出样例2
0
输入样例3
5
324097321 555675086 304655177 991244276 9980291
输出样例3
805306368
算法
按位贪心
由于异或运算各位是独立的,因此可以直接从高到底逐为考虑,尽可能让所有数的高位变成0。
对于某一位i,如果所有元素在这一位都是0或者1,那么一定可以找到一个x,让所有元素在这一位都为0。如果这一位既有0也有1,那最大值在这一位就只能是1了,当前位对最大值的贡献是2i,如果x的这一位决定让所有在该位是0的数在这一位是0,那么下一位考虑的就是所有在当前位为1的数(因为所有在该位为0的为异或之后在当前位是0了,绝对不可能是最大值);反之,如果x的这一位决定让所有在该位是1的数在这一位是0,那么下一位考虑的就是所有在当前位为0的数。
复杂度分析
时间复杂度
递归了30层,所有层的临时数组的元素个数加起来就是原始数组的n,因此时间复杂度为O(30n)。
空间复杂度
额外空间就是每层递归的临时数组,仍然是O(n)级别。
C++ 代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int n;
int dfs(vector<int>& a, int depth) {
if(depth < 0) return 0;
vector<int> zero, one;
for(int x: a) {
if(x>>depth&1) {
one.push_back(x);
}else {
zero.push_back(x);
}
}
if(one.empty()) {
// 没有1,当前位可以变成0
return dfs(zero, depth - 1);
}else if(zero.empty()) {
// 没有0,当前位也可以变成0
return dfs(one, depth - 1);
}
// 有0又有1,不管x这一位取什么,最大值的这一位都是1
return min(dfs(zero, depth - 1), dfs(one, depth - 1)) + (1<<depth);
}
int main() {
scanf("%d", &n);
vector<int> arr(n);
for(int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
printf("%d\n", dfs(arr, 30));
return 0;
}