From 3487b4512c822fe17083e2d3dada037450af943f Mon Sep 17 00:00:00 2001 From: AC_Oier Date: Mon, 14 Mar 2022 16:44:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8update:=20Modify=20599?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...10\344\270\255\347\255\211\357\274\211.md" | 130 ------------------ ...10\347\256\200\345\215\225\357\274\211.md" | 2 +- 2 files changed, 1 insertion(+), 131 deletions(-) delete mode 100644 "LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" diff --git "a/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" deleted file mode 100644 index 7e8d37fd..00000000 --- "a/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" +++ /dev/null @@ -1,130 +0,0 @@ -### 题目描述 - -这是 LeetCode 上的 **[2044. 统计按位或能得到最大值的子集数目]()** ,难度为 **中等**。 - -Tag : 「二进制枚举」、「位运算」、「回溯算法」 - - - -给你一个整数数组 $nums$ ,请你找出 $nums$ 子集 **按位或** 可能得到的 最大值 ,并返回按位或能得到最大值的 **不同非空子集的数目** 。 - -如果数组 $a$ 可以由数组 $b$ 删除一些元素(或不删除)得到,则认为数组 $a$ 是数组 $b$ 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。 - -对数组 $a$ 执行 按位或 ,结果等于 $a[0]$ `OR` $a[1]$ `OR` `...` `OR` $a[a.length - 1]$(下标从 $0$ 开始)。 - -示例 1: -``` -输入:nums = [3,1] - -输出:2 - -解释:子集按位或能得到的最大值是 3 。有 2 个子集按位或可以得到 3 : -- [3] -- [3,1] -``` -示例 2: -``` -输入:nums = [2,2,2] - -输出:7 - -解释:[2,2,2] 的所有非空子集的按位或都可以得到 2 。总共有 23 - 1 = 7 个子集。 -``` -示例 3: -``` -输入: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$ - ---- - -### 二进制枚举 - -令 $n$ 为 $nums$ 的长度,利用 $n$ 不超过 $16$,我们可以使用一个 `int` 数值来代指 $nums$ 的使用情况(子集状态)。 - -假设当前子集状态为 $state$,$state$ 为一个仅考虑低 $n$ 位的二进制数,当第 $k$ 位为 $1$,代表 $nums[k]$ 参与到当前的按位或运算,当第 $k$ 位为 $0$,代表 $nums[i]$ 不参与到当前的按位或运算。 - -在枚举这 $2^n$ 个状态过程中,我们使用变量 `max` 记录最大的按位或得分,使用 `ans` 记录能够取得最大得分的状态数量。 - -代码: -```Java -class Solution { - public int countMaxOrSubsets(int[] nums) { - int n = nums.length, mask = 1 << n; - int max = 0, ans = 0; - for (int s = 0; s < mask; s++) { - int cur = 0; - for (int i = 0; i < n; i++) { - if (((s >> i) & 1) == 1) cur |= nums[i]; - } - if (cur > max) { - max = cur; ans = 1; - } else if (cur == max) { - ans++; - } - } - return ans; - } -} -``` -* 时间复杂度:令 $nums$ 长度为 $n$,共有 $2^n$ 个子集状态,计算每个状态的按位或答案复杂度为 $O(n)。$整体复杂度为 $O(2^n * n)$ -* 空间复杂度:$O(1)$ - ---- - -### 回溯算法 - - - -代码: -```Java -class Solution { - int[] nums; - int max = 0, ans = 0; - public int countMaxOrSubsets(int[] _nums) { - nums = _nums; - dfs(0, 0); - return ans; - } - void dfs(int u, int val) { - if (u == nums.length) { - if (val > max) { - max = val; ans = 1; - } else if (val == max) { - ans++; - } - return ; - } - dfs(u + 1, val); - dfs(u + 1, val | nums[u]); - } -} -``` -* 时间复杂度:令 $nums$ 长度为 $n$,共有 $2^n$ 个子集状态。$整体复杂度为 $O(2^n * n)$ -* 空间复杂度:忽略递归带来的额外空间开销,复杂度为 $O(1)$ - ---- - -### 最后 - -这是我们「刷穿 LeetCode」系列文章的第 `No.2044` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 - -在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 - -为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 - -在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 - diff --git "a/LeetCode/591-600/599. \344\270\244\344\270\252\345\210\227\350\241\250\347\232\204\346\234\200\345\260\217\347\264\242\345\274\225\346\200\273\345\222\214\357\274\210\347\256\200\345\215\225\357\274\211.md" "b/LeetCode/591-600/599. \344\270\244\344\270\252\345\210\227\350\241\250\347\232\204\346\234\200\345\260\217\347\264\242\345\274\225\346\200\273\345\222\214\357\274\210\347\256\200\345\215\225\357\274\211.md" index c4aa04a4..a1bfaf7f 100644 --- "a/LeetCode/591-600/599. \344\270\244\344\270\252\345\210\227\350\241\250\347\232\204\346\234\200\345\260\217\347\264\242\345\274\225\346\200\273\345\222\214\357\274\210\347\256\200\345\215\225\357\274\211.md" +++ "b/LeetCode/591-600/599. \344\270\244\344\270\252\345\210\227\350\241\250\347\232\204\346\234\200\345\260\217\347\264\242\345\274\225\346\200\273\345\222\214\357\274\210\347\256\200\345\215\225\357\274\211.md" @@ -74,7 +74,7 @@ class Solution { } } ``` -* 时间复杂度:$O(n)$ +* 时间复杂度:$O(n + m)$ * 空间复杂度:$O(n)$ --- From 60e1a3310e98f56e0ad9bb2b7027841ee1c4cf91 Mon Sep 17 00:00:00 2001 From: AC_Oier Date: Tue, 15 Mar 2022 09:52:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8feat:=20Add=202044?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Index/DFS.md | 1 + ...33\345\210\266\346\236\232\344\270\276.md" | 1 + .../\344\275\215\350\277\220\347\256\227.md" | 1 + "Index/\347\212\266\345\216\213 DP.md" | 1 + ...10\344\270\255\347\255\211\357\274\211.md" | 181 ++++++++++++++++++ 5 files changed, 185 insertions(+) create mode 100644 "LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" diff --git a/Index/DFS.md b/Index/DFS.md index f552e7c0..0021106b 100644 --- a/Index/DFS.md +++ b/Index/DFS.md @@ -32,4 +32,5 @@ | [1609. 奇偶树](https://leetcode-cn.com/problems/even-odd-tree/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/even-odd-tree/solution/gong-shui-san-xie-yi-ti-shuang-jie-bfs-d-kuyi/) | 中等 | 🤩🤩🤩🤩🤩 | | [1723. 完成所有工作的最短时间](https://leetcode-cn.com/problems/find-minimum-time-to-finish-all-jobs/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/find-minimum-time-to-finish-all-jobs/solution/gong-shui-san-xie-yi-ti-shuang-jie-jian-4epdd/) | 困难 | 🤩🤩🤩 | | [1766. 互质树](https://leetcode-cn.com/problems/tree-of-coprimes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/tree-of-coprimes/solution/bu-tai-yi-yang-de-dfs-ji-lu-suo-you-zui-d3xeu/) | 困难 | 🤩🤩🤩🤩 | +| [2044. 统计按位或能得到最大值的子集数目](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/solution/by-ac_oier-dos6/) | 困难 | 🤩🤩🤩🤩 | diff --git "a/Index/\344\272\214\350\277\233\345\210\266\346\236\232\344\270\276.md" "b/Index/\344\272\214\350\277\233\345\210\266\346\236\232\344\270\276.md" index fc03e911..99d559b9 100644 --- "a/Index/\344\272\214\350\277\233\345\210\266\346\236\232\344\270\276.md" +++ "b/Index/\344\272\214\350\277\233\345\210\266\346\236\232\344\270\276.md" @@ -2,4 +2,5 @@ | ------------------------------------------------------------ | ------------------------------------------------------------ | ---- | -------- | | [1239. 串联字符串的最大长度](https://leetcode-cn.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters/solution/gong-shui-san-xie-yi-ti-san-jie-jian-zhi-nfeb/) | 中等 | 🤩🤩🤩 | | [1601. 最多可达成的换楼请求数目](https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/maximum-number-of-achievable-transfer-requests/solution/gong-shui-san-xie-er-jin-zhi-mei-ju-by-a-enef/) | 中等 | 🤩🤩🤩🤩 | +| [2044. 统计按位或能得到最大值的子集数目](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/solution/by-ac_oier-dos6/) | 困难 | 🤩🤩🤩🤩 | diff --git "a/Index/\344\275\215\350\277\220\347\256\227.md" "b/Index/\344\275\215\350\277\220\347\256\227.md" index 43489d02..0b65c9e0 100644 --- "a/Index/\344\275\215\350\277\220\347\256\227.md" +++ "b/Index/\344\275\215\350\277\220\347\256\227.md" @@ -17,5 +17,6 @@ | [526. 优美的排列](https://leetcode-cn.com/problems/beautiful-arrangement/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/beautiful-arrangement/solution/gong-shui-san-xie-xiang-jie-liang-chong-vgsia/) | 中等 | 🤩🤩🤩 | | [1178. 猜字谜](https://leetcode-cn.com/problems/number-of-valid-words-for-each-puzzle/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/number-of-valid-words-for-each-puzzle/solution/xiang-jin-zhu-shi-xiang-jie-po-su-wei-yu-3cr2/) | 困难 | 🤩🤩🤩🤩 | | [1711. 大餐计数](https://leetcode-cn.com/problems/count-good-meals/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/count-good-meals/solution/gong-shui-san-xie-xiang-jie-san-chong-gu-nn4f/) | 中等 | 🤩🤩🤩 | +| [2044. 统计按位或能得到最大值的子集数目](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/solution/by-ac_oier-dos6/) | 困难 | 🤩🤩🤩🤩 | | [剑指 Offer 15. 二进制中1的个数](https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof/solution/gong-shui-san-xie-yi-ti-si-jie-wei-shu-j-g9w6/) | 简单 | 🤩🤩🤩 | diff --git "a/Index/\347\212\266\345\216\213 DP.md" "b/Index/\347\212\266\345\216\213 DP.md" index 46ea5c45..55fcd1c1 100644 --- "a/Index/\347\212\266\345\216\213 DP.md" +++ "b/Index/\347\212\266\345\216\213 DP.md" @@ -3,4 +3,5 @@ | [526. 优美的排列](https://leetcode-cn.com/problems/beautiful-arrangement/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/beautiful-arrangement/solution/gong-shui-san-xie-xiang-jie-liang-chong-vgsia/) | 中等 | 🤩🤩🤩🤩🤩 | | [847. 访问所有节点的最短路径](https://leetcode-cn.com/problems/shortest-path-visiting-all-nodes/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/shortest-path-visiting-all-nodes/solution/gong-shui-san-xie-yi-ti-shuang-jie-bfs-z-6p2k/) | 困难 | 🤩🤩🤩🤩🤩 | | [1994. 好子集的数目](https://leetcode-cn.com/problems/the-number-of-good-subsets/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/the-number-of-good-subsets/solution/gong-shui-san-xie-zhuang-ya-dp-yun-yong-gz4w5/) | 困难 | 🤩🤩🤩🤩 | +| [2044. 统计按位或能得到最大值的子集数目](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/) | [LeetCode 题解链接](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/solution/by-ac_oier-dos6/) | 困难 | 🤩🤩🤩🤩 | diff --git "a/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" "b/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" new file mode 100644 index 00000000..70bb7833 --- /dev/null +++ "b/LeetCode/2041-2050/2044. \347\273\237\350\256\241\346\214\211\344\275\215\346\210\226\350\203\275\345\276\227\345\210\260\346\234\200\345\244\247\345\200\274\347\232\204\345\255\220\351\233\206\346\225\260\347\233\256\357\274\210\344\270\255\347\255\211\357\274\211.md" @@ -0,0 +1,181 @@ +### 题目描述 + +这是 LeetCode 上的 **[2044. 统计按位或能得到最大值的子集数目](https://leetcode-cn.com/problems/count-number-of-maximum-bitwise-or-subsets/solution/by-ac_oier-dos6/)** ,难度为 **中等**。 + +Tag : 「二进制枚举」、「位运算」、「DFS」、「状压 DP」 + + + +给你一个整数数组 $nums$ ,请你找出 $nums$ 子集 **按位或** 可能得到的 **最大值** ,并返回按位或能得到最大值的 **不同非空子集的数目** 。 + +如果数组 $a$ 可以由数组 $b$ 删除一些元素(或不删除)得到,则认为数组 $a$ 是数组 $b$ 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。 + +对数组 $a$ 执行 **按位或** ,结果等于 $a[0]$ `OR` $a[1]$ `OR` `...` `OR` $a[a.length - 1]$(下标从 $0$ 开始)。 + +示例 1: +``` +输入:nums = [3,1] + +输出:2 + +解释:子集按位或能得到的最大值是 3 。有 2 个子集按位或可以得到 3 : +- [3] +- [3,1] +``` +示例 2: +``` +输入:nums = [2,2,2] + +输出:7 + +解释:[2,2,2] 的所有非空子集的按位或都可以得到 2 。总共有 23 - 1 = 7 个子集。 +``` +示例 3: +``` +输入: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$ + +--- + +### 二进制枚举 + +令 $n$ 为 $nums$ 的长度,利用 $n$ 不超过 $16$,我们可以使用一个 `int` 数值来代指 $nums$ 的使用情况(子集状态)。 + +假设当前子集状态为 $state$,$state$ 为一个仅考虑低 $n$ 位的二进制数,当第 $k$ 位为 $1$,代表 $nums[k]$ 参与到当前的按位或运算,当第 $k$ 位为 $0$,代表 $nums[i]$ 不参与到当前的按位或运算。 + +在枚举这 $2^n$ 个状态过程中,我们使用变量 `max` 记录最大的按位或得分,使用 `ans` 记录能够取得最大得分的状态数量。 + +代码: +```Java +class Solution { + public int countMaxOrSubsets(int[] nums) { + int n = nums.length, mask = 1 << n; + int max = 0, ans = 0; + for (int s = 0; s < mask; s++) { + int cur = 0; + for (int i = 0; i < n; i++) { + if (((s >> i) & 1) == 1) cur |= nums[i]; + } + if (cur > max) { + max = cur; ans = 1; + } else if (cur == max) { + ans++; + } + } + return ans; + } +} +``` +* 时间复杂度:令 $nums$ 长度为 $n$,共有 $2^n$ 个子集状态,计算每个状态的按位或得分的复杂度为 $O(n)$。整体复杂度为 $O(2^n * n)$ +* 空间复杂度:$O(1)$ + + +--- + +### 状压 DP + +为了优化解法一中「每次都要计算某个子集的得分」这一操作,我们可以将所有状态的得分记下来,采用「动态规划」思想进行优化。 + +需要找到当前状态 $state$ 可由哪些状态转移而来:假设当前 $state$ 中处于最低位的 $1$ 位于第 $idx$ 位,首先我们可以使用 `lowbit` 操作得到「仅保留第 $idx$ 的 $1$ 所对应的数值」,记为 $lowbit$,那么显然对应的状态方程为: + +$$ +f[state] = f[state - lowbit] \wedge nums[idx] +$$ + +再配合我们从小到大枚举所有的 $state$ 即可确保计算 $f[state]$ 时所依赖的 $f[state - lowbit]$ 已被计算。 + +最后为了快速知道数值 $lowbit$ 最低位 $1$ 所处于第几位(也就是 $idx$ 为何值),我们可以利用 $nums$ 长度最多不超过 $16$ 来进行「打表」预处理。 + +代码: +```Java +class Solution { + static Map map = new HashMap<>(); + static { + for (int i = 0; i < 20; i++) map.put((1 << i), i); + } + public int countMaxOrSubsets(int[] nums) { + int n = nums.length, mask = 1 << n; + int[] f = new int[mask]; + int max = 0, ans = 0; + for (int s = 1; s < mask; s++) { + int lowbit = (s & -s); + int prev = s - lowbit, idx = map.get(lowbit); + f[s] = f[prev] | nums[idx]; + if (f[s] > max) { + max = f[s]; ans = 1; + } else if (f[s] == max) { + ans++; + } + } + return ans; + } +} +``` +* 时间复杂度:$O(2^n)$ +* 空间复杂度:$O(2^n)$ + +--- + +### DFS + +解法一将「枚举子集/状态」&「计算状态对应的得分」两个过程分开进行,导致了复杂度上界为 $O(2^n * n)$。 + +事实上,我们可以在「枚举子集」的同时「计算相应得分」,设计 `void dfs(int u, int val)` 的 `DFS` 函数来实现「爆搜」,其中 $u$ 为当前的搜索到 $nums$ 的第几位,$val$ 为当前的得分情况。 + +对于任意一位 $x$ 而言,都有「选」和「不选」两种选择,分别对应了 `dfs(u + 1, val | nums[x])` 和 `dfs(u + 1, val)` 两条搜索路径,在搜索所有状态过程中,使用全局变量 `max` 和 `ans` 来记录「最大得分」以及「取得最大得分的状态数量」。 + +该做法将多条「具有相同前缀」的搜索路径的公共计算部分进行了复用,从而将算法复杂度下降为 $O(2^n)$。 + +代码: +```Java +class Solution { + int[] nums; + int max = 0, ans = 0; + public int countMaxOrSubsets(int[] _nums) { + nums = _nums; + dfs(0, 0); + return ans; + } + void dfs(int u, int val) { + if (u == nums.length) { + if (val > max) { + max = val; ans = 1; + } else if (val == max) { + ans++; + } + return ; + } + dfs(u + 1, val); + dfs(u + 1, val | nums[u]); + } +} +``` +* 时间复杂度:令 $nums$ 长度为 $n$,共有 $2^n$ 个子集状态。整体复杂度为 $O(2^n)$ +* 空间复杂度:忽略递归带来的额外空间开销,复杂度为 $O(1)$ + +--- + +### 最后 + +这是我们「刷穿 LeetCode」系列文章的第 `No.2044` 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。 + +在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。 + +为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。 + +在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。 +