2044_统计按位或能得到最大值的子集数目

难度:中等

题目

给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 。

如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。

对数组 a 执行 按位或 ,结果等于 a[0] OR a[1] OR … OR a[a.length - 1](下标从 0 开始)。

示例

示例一:

输入:nums = [3,1]
输出:2
解释:子集按位或能得到的最大值是 3 。有 2 个子集按位或可以得到 3 :

  • [3]
  • [3,1]

示例二:

输入:nums = [2,2,2]
输出:7
解释:[2,2,2] 的所有非空子集的按位或都可以得到 2 。总共有 23 - 1 = 7 个子集。

示例三:

输入:nums = [3,2,1,5]
输出:6
解释:子集按位或可能的最大值是 7 。有 6 个子集按位或可以得到 7 :

  • [3,5]
  • [3,1,5]
  • [3,2,5]
  • [3,2,1,5]
  • [2,5]
  • [2,1,5]

提示

  • 1 <= nums.length <= 16
  • 1 <= nums[i] <= 10^5

解题

所有元素做或运算的结果肯定是最大值,所以预先计算最大值。

1
2
3
4
int max = 0;
for (int num:nums){
max |= num;
}

因为每个位置都可选可不选,所以利用回溯来求解

1
2
3
4
5
6
7
public static int dfs(int curIndex, int[] nums, int curValue, int max){
if (curIndex==nums.length){
return curValue==max?1:0;
}
// 返回选与不选当前项的结果之和
return dfs(curIndex+1,nums,curValue|nums[curIndex],max) + dfs(curIndex+1,nums,curValue,max);
}

可以进行优化,比如当我们搜索到某个下标 i 时,如果计算结果等于最大值了,就没有必要继续往下搜索了,这时候后面的元素不管选还是不选,最终的结果一定是等于最大值的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void dfs(int[] nums, int i, int or, int max) {
if (or == max) {
// 等于最大值了,没有继续搜索的必要,直接计算有多少种方案
ans += 1 << (nums.length - i);
return;
}
if (i == nums.length) {
return;
}

// 当前元素选或者不选
dfs(nums, i + 1, or | nums[i], max);
dfs(nums, i + 1, or, max);
}