3599. 划分数组得到最小 XOR
考点
- 划分DP
- 前缀和
- 二分
思路
划分DP
直接看代码即可,要注意这里异或值可能会非常大,0x3f3f3f3f不够用,建议直接上INT_MAX
我代码用0x7f7f7f7f发现也可以:
1 | class Solution { |
当然也可以滚动数组滚掉一维:
1 | class Solution { |
二分+DP
本题用二分并不优,因为内部还要用一次DP,锻炼码力也可以写一下
先证明一下二分的可行性,尽管异或值本身并不单调,但二分答案根本不需要“异或值本身单调” 二分需要的是一个 布尔函数 的单调性:
F(T) = 是否存在一种划分,使得所有段的 XOR 都 ≤ T
只要能证明: 如果 F(T) = true,那对所有 T' > T 也有
F(T') = true, 那就可以二分 T。 这里的“单调性”是 F(T) 随着 T
的单调(从 false→true),跟异或怎么跳来跳去无关。
1. 命题描述
题目:给定数组 nums 和整数 k,把它划成 k
段, 每段的 XOR 记为 xor_seg_1, xor_seg_2, ..., xor_seg_k,
我们想最小化 max(xor_seg_1, ..., xor_seg_k)。
定义一个函数:
F(T) = 是否存在一种把数组划成 k 段的方案, 使得每一段的 XOR 都 ≤ T。
那么题目要找的答案就是所有满足 F(T)=true 的 T 的最小值。
二分需要的条件:
如果 F(T) = true,则对所有
T' ≥ T,F(T') 也必须是 true。
这就是所谓的“二分单调性”。
2. 证明 F(T) 的单调性(这跟 XOR 本身无关)
假设对某个 T,F(T) = true。 这意味着:存在某种划分方案 S,把数组分成 k 段:
- 这一种方案 S 下,每一段的 XOR 都 ≤ T。
现在随便取一个更大的数 T' ≥ T。
同一套划分方案 S,所有段的 XOR 仍然是当初那几个值,没变: 它们原来 ≤ T,所以当然也 ≤ T'(因为 T' 更大)。
所以:
- 用同一个方案 S,F(T') 也为 true。
这说明:
T 满足 ⇒ 所有更大的 T' 也满足, F 在 T 上是从 false→true 一次性跳过去,就不会来回乱跳。
你可以把这个逻辑注意一下: 我们二分的是 “允许的最大值 T 有多松”, 约束越松(T 越大),方案只会更多,不会更少。 这个跟“异或值自己是不是递增”一点关系都没有。
3. 举例
比如数组 nums = [1, 2, 3],k = 2。
我们可以列一下所有划分方案和它们的 “最大 XOR”:
[1] | [2,3]- XOR:
1和2^3=1,max = 1
- XOR:
[1,2] | [3]- XOR:
1^2=3和3,max = 3
- XOR:
所以所有可能的“最大 XOR”是 {1, 3},答案是 min{1,3} = 1。
现在看 F(T):
- T = 0: 没有任何划分方案能让所有段 XOR ≤ 0 ⇒ F(0) = false
- T = 1: 方案 1 里 max XOR = 1 ≤ 1 ⇒ F(1) = true
- T = 2: 方案 1 里 max XOR = 1 ≤ 2 ⇒ F(2) = true
- T = 3: 方案 1、2 都可以 ⇒ F(3) = true
所以:
1 | T: 0 1 2 3 ... |
这就是典型的 0,0,...,0,1,1,1,1... 的模式,可以二分
二分的思路如下:
二分主体思路
我们现在要二分的是一个上界 limit:
1 F(limit) = 是否存在一种把数组划分成恰好 k 段的方案,使得每一段的 XOR 都 ≤ limit
如果 F(limit) 为真,那么同一套划分在更大的
limit' 下也一定合法,所以:
1 F(limit) = true ⇒ 对所有 limit' ≥ limit, F(limit') 也为 true
这个布尔函数对 limit 是单调的,可以二分
二分区间可以取:
lo = 0hi = (1 << 30) - 1(因为nums[i] ≤ 1e9 < 2^30,任何子数组 XOR 都 < 2^30)
编写 check(limit)
用一个布尔 DP:
dp[i][c]:前i个数(nums[0..i-1])能不能被划分成恰好c段,且每一段的 XOR 都 ≤limit
初始条件:
dp[0][0] = truedp[0][c>0] = false
转移:
- 让最后一段是
[l .. i-1](1-based 就是[l..i]),它的 XOR 记为xor(l..i-1) - 如果:
dp[l][c-1] == true,并且xor(l..i-1) ≤ limit
- 那么:
1 | dp[i][c] = true; |
这样最后 dp[n][k] 就是 F(limit)
的真假。
AC代码如下:
1 | class Solution { |