当前位置: 首页 > news >正文

最大异或对问题

最大异或对问题:从暴力到Trie树的高效解法

题目

二、异或运算的核心性质

异或(XOR,记为 ⊕\oplus)是一种位运算,其规则为:

  • 两个二进制位相同时,结果为0000⊕0=00 \oplus 0 = 000=01⊕1=01 \oplus 1 = 011=0
  • 两个二进制位不同时,结果为1110⊕1=10 \oplus 1 = 101=11⊕0=11 \oplus 0 = 110=1

关键观察:异或结果的大小由二进制高位决定。例如,若两个数的第 kkk 位(从000开始计数)异或为111,而其他数的第 kkk 位异或为000,则前者结果一定更大。因此,最大化异或值的核心是:让更高位的二进制位尽可能异或为111

三、暴力解法:思路与局限

思路

枚举所有可能的数对 (Ai,Aj)(A_i, A_j)(Ai,Aj)i<ji < ji<j),计算 Ai⊕AjA_i \oplus A_jAiAj 并记录最大值。

代码示意

int maxXor = 0;
for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {maxXor = max(maxXor, a[i] ^ a[j]);}
}

复杂度分析

  • 时间复杂度:O(N2)O(N^2)O(N2)。当 N=105N = 10^5N=105 时,运算次数约为 101010^{10}1010,远超计算机每秒约 10810^8108 次运算的能力,必然超时。
  • 空间复杂度:O(1)O(1)O(1)

结论:暴力解法仅适用于 N≤1000N \leq 1000N1000 的小规模数据,对本题不适用。

四、优化思路:Trie树的妙用

为了降低时间复杂度,我们需要一种能快速找到“与当前数异或最大的数”的方法。Trie树(字典树)正是解决这一问题的理想工具。

核心思想

  1. 二进制高位优先存储:将每个数的二进制表示(从最高位到最低位)存入Trie树,利用Trie的前缀共享特性高效存储。
  2. 贪心查询:对每个数 AiA_iAi,在Trie树中寻找能使其异或结果最大的数——即每一位尽可能选择与 AiA_iAi 对应位相反的二进制位。

五、Trie树的设计与实现

1. Trie节点结构

Trie树的每个节点仅需存储两个子节点,分别对应二进制位000111

struct TrieNode {TrieNode* children[2]; // 0和1两个子节点TrieNode() {children[0] = children[1] = nullptr; // 初始化子节点为空}
};

2. 插入操作:将数存入Trie树

将一个数的二进制表示从最高位到最低位插入Trie树(因 Ai<231A_i < 2^{31}Ai<231,最高位为第303030位):

  • 提取第 iii 位的二进制值:(num>>i)&1(num >> i) \& 1(num>>i)&1
  • 若对应子节点不存在,则创建新节点
  • 沿当前位对应的子节点继续深入
void insert(TrieNode* root, int num) {TrieNode* node = root;for (int i = 30; i >= 0; --i) { // 从最高位(30)到最低位(0)int bit = (num >> i) & 1;   // 提取第i位if (!node->children[bit]) {node->children[bit] = new TrieNode(); // 创建新节点}node = node->children[bit]; // 移动到子节点}
}

3. 查询操作:寻找最大异或值

对给定数 numnumnum,在Trie树中寻找能使其异或结果最大的数:

  • 对每一位,优先选择与 numnumnum 当前位相反的子节点(使该位异或为111
  • 若相反位的子节点不存在,则选择相同位的子节点
  • 累加每一位的贡献(1<<i1 << i1<<i 表示第iii位为111时的数值)
int query(TrieNode* root, int num) {TrieNode* node = root;int max_xor = 0;for (int i = 30; i >= 0; --i) {int bit = (num >> i) & 1;int desired_bit = 1 - bit; // 希望找到相反的位if (node->children[desired_bit]) {max_xor |= (1 << i);    // 该位异或为1,计入结果node = node->children[desired_bit];} else {node = node->children[bit]; // 只能选择相同位}}return max_xor;
}

4. 整体流程

  1. 初始化Trie树,插入第一个数
  2. 对剩余每个数:
    • 查询当前Trie树中能与它形成的最大异或值
    • 更新全局最大值
    • 将该数插入Trie树(供后续数查询)
int main() {int n;cin >> n;vector<int> a(n);for (int i = 0; i < n; ++i) {cin >> a[i];}TrieNode* root = new TrieNode();insert(root, a[0]); // 插入第一个数int max_val = 0;for (int i = 1; i < n; ++i) {max_val = max(max_val, query(root, a[i])); // 查询并更新最大值insert(root, a[i]); // 插入当前数}cout << max_val << endl;return 0;
}

六、复杂度分析

  • 时间复杂度
    插入和查询每个数都需要处理313131个二进制位(000~303030),因此总时间复杂度为 O(N×31)O(N \times 31)O(N×31),即 O(N)O(N)O(N)313131为常数)。对于 N=105N = 10^5N=105,总操作约为 3.1×1063.1 \times 10^63.1×106,完全满足时间要求。

  • 空间复杂度
    最坏情况下,每个数的二进制位都不共享前缀,空间复杂度为 O(N×31)O(N \times 31)O(N×31),即 O(N)O(N)O(N),可接受。

七、样例解析

以输入 3 1 2 3 为例,详细步骤如下:

  1. 二进制表示
    1=0b011 = 0b011=0b01(简化为222位,实际处理313131位)
    2=0b102 = 0b102=0b10
    3=0b113 = 0b113=0b11

  2. 插入第一个数111
    Trie树路径:根 → 000(第111位) → 111(第000位)

  3. 处理第二个数222

    • 查询:222的二进制为101010,从高位开始:
      111位是111 → 优先找000(存在),贡献 1<<1=21 << 1 = 21<<1=2
      000位是000 → 优先找111(存在),贡献 1<<0=11 << 0 = 11<<0=1
      总异或值:2+1=32 + 1 = 32+1=3
    • 更新max_valmax\_valmax_val333,插入222到Trie树
  4. 处理第三个数333

    • 查询:333的二进制为111111,从高位开始:
      111位是111 → 找000(存在),贡献 222
      000位是111 → 找000(不存在,只能找111),无贡献
      总异或值:222
    • max_valmax\_valmax_val仍为333,插入333到Trie树
  5. 最终输出333

八、总结

最大异或对问题的高效解法体现了两个核心思想:

  1. 二进制高位优先:利用异或运算的特性,优先保证高位为111以最大化结果。
  2. Trie树优化:将二进制存储与查询的复杂度从 O(N)O(N)O(N) 降至 O(31)O(31)O(31),实现整体线性时间复杂度。

这种思路不仅适用于本题,还可推广到“最大异或子集”“异或前缀和”等类似问题,是信奥中处理二进制位运算的重要技巧。


文章转载自:

http://cNzX8IHa.Ljmbd.cn
http://csklN25c.Ljmbd.cn
http://0amcgxzA.Ljmbd.cn
http://xmurRkbj.Ljmbd.cn
http://2BNYeP4p.Ljmbd.cn
http://tweUY5fQ.Ljmbd.cn
http://ZYRYnQ1i.Ljmbd.cn
http://V60MTJh6.Ljmbd.cn
http://iK4Jennd.Ljmbd.cn
http://ApxmZy9Q.Ljmbd.cn
http://9KJ6N2Bs.Ljmbd.cn
http://yiaZ8cNu.Ljmbd.cn
http://cqT6oMnI.Ljmbd.cn
http://fK1R9E8g.Ljmbd.cn
http://uw5043Dl.Ljmbd.cn
http://4pnlGUfY.Ljmbd.cn
http://H9Suq2rm.Ljmbd.cn
http://MMp4PWOZ.Ljmbd.cn
http://uBvJ2c2B.Ljmbd.cn
http://YMDOXnfv.Ljmbd.cn
http://Gd7QUO4t.Ljmbd.cn
http://bhtru5fG.Ljmbd.cn
http://TMtf0yRK.Ljmbd.cn
http://c9rM1AhL.Ljmbd.cn
http://Xi5pp5nm.Ljmbd.cn
http://FEhABEgT.Ljmbd.cn
http://kTVHVOpg.Ljmbd.cn
http://afQuXsjS.Ljmbd.cn
http://NBzqbS30.Ljmbd.cn
http://XXjipkA2.Ljmbd.cn
http://www.dtcms.com/a/375869.html

相关文章:

  • Tess-two - Tess-two 文字识别(Tess-two 概述、Tess-two 文字识别、补充情况)
  • hot100 之移动零-283(双指针)
  • APP隐私合规评估测试核心要点与第三方APP检测全流程解析
  • ARM汇编与栈操作指南
  • 在 Keil 中将 STM32 工程下载到 RAM 进行调试运行
  • 高效数据操作:详解MySQL UPDATE中的CASE条件更新与性能优化
  • 构建企业级Selenium爬虫:基于隧道代理的IP管理架构
  • Nginx限流与防爬虫与安全配置方案
  • YOLO11训练自己数据集的注意事项、技巧
  • Kafka面试精讲 Day 13:故障检测与自动恢复
  • Linux学习——管理网络安全(二十一)
  • 平衡车 -- PID
  • 【ComfyUI】Flux Krea 微调完美真实照片生成
  • dp类相关问题(1):区间dp
  • TensorFlow 2.x 核心 API 与模型构建:从入门到实践
  • 华清远见25072班网络编程学习day2
  • 【论文写作】--网络与信息安全顶刊顶会
  • 【人工智能99问】如何基于QWen3进行LoRA微调?(38/99)
  • JAVA Predicate
  • 自动驾驶中的传感器技术41——Radar(2)
  • Netty HandlerContext 和 Pipeline
  • Stuns in Singapore!中新赛克盛大亮相ISS World Asia 2025
  • 开始 ComfyUI 的 AI 绘图之旅-LoRA(五)
  • 字符函数和字符串函数 last part
  • win安装多个mysql,免安装mysql
  • 开源项目_强化学习股票预测
  • Shell 脚本基础:从语法到实战全解析
  • Nginx如何部署HTTP/3
  • 解一元三次方程
  • A股大盘数据-20250909分析