CF1285 --- Dr. Evil Underscores

CF1285 --- Dr. Evil Underscores

题干

Today as a friendship gift, Bakry gave Badawy \(n\) integers \(a_1,a_2, \cdots ,a_n\) and challenged him to choose an integer \(X\) such that the value \(\mathop{max}\limits_{1\leq i\leq n}(a_i \oplus X)\) is minimum possible, where \(\oplus\) denotes the bitwise XOR operation.

\(\mathcal{Input}\)
The first line contains integer \(n (1\leq n\leq 10^5)\).
The second line contains \(n\) integers \(a_1,a_2, \cdots ,a_n\) \((0\leq a_i\leq 2^{30}−1).\)
\(\mathcal{Output}\)
Print one integer — the minimum possible value of \(\mathop{max}\limits_{1\leq i\leq n}(a_i \oplus X)\)
\(\mathcal{Example}\)

\(Case_1\)
\(Input\)
3
1 2 3
\(Output\)
2
\(Case_2\)
\(Input\)
2
1 5
\(Output\)
4
\(\mathcal{Note}\)
In the first sample, we can choose \(X=3\).
In the second sample, we can choose \(X=5\).
\(\mathcal{Tag}\)
bitmasks divide and conquer dfs and similar dp *1800

思路分析

 这题就是选取一个\(X\)值,让序列\(a_1,a_2, \cdots ,a_n\)异或他之后的最大值最小。实际上,这个题目是有两个关键词。

  • 对一个序列进行异或,或者区间异或 \(\rightarrow\) \(\mathcal{Trie\; Tree}\)(字典树)
  • 最大值最小问题 \(\rightarrow\) binary search or conquer and divide.

暴力想法

 这题想的时候,由于信息熵肯定要遍历序列的,然后考虑序列中的最大值,我估计是\(\mathcal{O}(nlog_n)\)的复杂度,因此我直接考虑的是进行二分。但是没有找到分治的split点。因此没有想出较好的方法。

算法优化

 这题主要抓住一个问题,即我们要让最大值最小,因此得考虑最坏情况下的最小值。因此抽象来,就是我们不断做出策略,求解最终策略下最坏情况的最大值。(为什么是不断做出策略呢?)
 有关异或的性质,我们知道当该序列中,某位上所有都为0或1时,我们可以通过制定\(X\)在这位的值,让他在异或序列时,最终的结果都为0.我们假设序列中某\(bit\)为1的所有数为数组\(a_{on}\),某\(bit\)为0的所有数为数组\(a_{off}\).因此当\(a_{on},a_{off}\)都不为空时,该\(bit\)位的值为\(2^{bit}\)。因为不论\(X\)在该位如何设置0或1,一定存在一个数组,让\(X\)异或之后该位的值为\(2^bit\).又因为我们要进行贪心寻找---从高位往地位找,这样才能保证我们在某数组为空时舍弃另一数组的正确性。在都不为空时进行分治。总结如下:

\[dfs(a, bit) = \left\{ \begin{array}[ll] 1dfs(a_{on}, bit-1) &if\; a_{off} = \emptyset\\ dfs(a_{off}, bit-1) &if\; a_{on} = \emptyset\\ min(dfs(a_{on}, bit-1), dfs(a_{off}, bit-1)) + (1 << bit) &if\; a_{off} \not= \emptyset\; and\; a_{on} \not= \emptyset \\ \end{array}\right. \]

 写出这个表达式之后,求解就非常简单了~ 代码如下

代码

#include<bits/stdc++.h>
using namespace std;

using VI = vector<int>;

int dfs(VI& a, int bit = 30){
    if (bit < 0 || a.empty()) return 0;
    VI on, off;

    for (int x : a){
        if ((x >> bit) & 1) on.push_back(x);
        else off.push_back(x);
    }

    if (on.empty()) return dfs(off, bit - 1);
    if (off.empty()) return dfs(on, bit - 1);

    return min(dfs(on, bit - 1), dfs(off, bit - 1)) + (1 << bit);
}


int main(){
    int n; cin >> n;
    VI a(n);
    for (auto& e : a) cin >> e;
    cout << dfs(a) << endl;
    return 0;
}

 这里没有考虑到 \(\mathcal{Trie\; Tree}\)(字典树)的解法,后续可以专门讨论下。

posted @ 2020-05-01 15:06  Last_Whisper  阅读(202)  评论(0编辑  收藏  举报