【算法学习笔记】28:Meet In The Middle优化

server/2024/10/9 17:26:21/

1 定义

当一个问题使用时间复杂度为 O ( e x p r ( n ) ) O(expr(n)) O(expr(n))会超时或者爆内存时,如果它存在这样的性质,可以分别对折半后的前后两个 n 2 \frac{n}{2} 2n的子问题进行搜索,并能根据这两个子问题的解,在 < O ( e x p r ( n ) ) < O(expr(n)) <O(expr(n))的时间内得到原问题的解时,就可以分别处理两个折半后的子问题,然后用两个子问题计算原问题的结果,称为Meet In The Middle。

这样就把时间复杂度从 O ( e x p r ( n ) ) O(expr(n)) O(expr(n))变成了 O ( 2 ⋅ e x p r ( n 2 ) ) O(2 \cdot expr(\frac{n}{2})) O(2expr(2n)),可能就过了,往往用在比较暴力的问题上,数据范围比较小可以暴力但却不能纯暴力通过。

2 例题

2.1 LC1755. 最接近目标值的子序列和

由于子序列就是这个序列里每个元素存在或者不存在(二元性问题),并且数据范围是<=40 < bits of ULL,所以可以用ULL当bitset来存每个元素pick或者not pick。

然后就可以暴力里找出 2 n 2^n 2n种选法的子序列和,放在sums里,其中 s u m s [ m a s k ] sums[mask] sums[mask]就是把 m a s k mask mask的每个为1的比特 i i i拆出来,所有满足条件的 i i i对应的 n u m s [ i ] nums[i] nums[i]的加和。

这个过程可以稍微优化一下,考虑到加法结合律a + b + c = (a + b) + c,只要算过(a + b)了只要+c就能算出a + b + c。

比如一共n位, i i i从低位(1<<0这一位)到高位(1<<(n-1)这一位),每次计算 i i i这一位为1,并且 > i >i >i的位置全都为0的情况,此时 < i <i <i的所有情况的加和都已经算出来过了,只要遍历一遍这些 s u b m a s k sub_mask submask,把这些已经算好的 s u m s [ s u b m a s k ] sums[sub_mask] sums[submask]加上 n u m s [ i ] nums[i] nums[i]就是 s u m s [ s u b m a s k ∣ 1 < < i ] sums[sub_mask | 1 << i] sums[submask∣1<<i]了。

得到所有 s u m s sums sums之后,只要排序在进行二分查找就可以找到最接近目标值的子序列和了:

class Solution {
public:int minAbsDifference(vector<int>& nums, int goal) {int n = nums.size();vector<int> sums(1ULL << n, 0);for (int i = 0; i < n; i ++ ){auto cur_bit = 1ULL << i;for (auto sub_mask = 0ULL; sub_mask < cur_bit; sub_mask ++ ){sums[cur_bit | sub_mask] = sums[sub_mask] + nums[i];}}sort(sums.begin(), sums.end());int l = 0, r = sums.size() - 1;while (l < r){int mid = l + r + 1 >> 1;if (sums[mid] <= goal) l = mid;else r = mid - 1;}int res = abs(goal - sums[l]);if (l + 1 < sums.size()) res = min(res, abs(goal - sums[l + 1]));return res;}
};

但是这样对 n < = 40 n <= 40 n<=40这个范围还是过于暴力了,数据量大点就过不了了。

考虑到把区间拆成左右两半的话,目标子序列要么全存在于左侧或者右侧区间(子问题),要么是左区间的某个子序列和右区间的某个子序列拼在一起,对应sums也是两者的sums加和(可以从两个子问题的结果得到)。

得到的方式可以用双指针,对于左右排好序的sums,两个指针分别指向左区间开头和右区间末尾,每次 < g o a l < goal <goal就把左侧区间指针往右移动,每次 > g o a l > goal >goal就把右侧区间指针往左移动,这样扫一遍就能在 O ( 2 ⋅ 2 n 2 ) O(2 \cdot 2^{\frac{n}{2}}) O(222n)里从两个子问题得到结果。

又知,左右两个子区间排序的时间复杂度是 n 2 ⋅ l o g ( n 2 ) \frac{n}{2} \cdot log(\frac{n}{2}) 2nlog(2n),这样所有的部分都没有对两个子问题进行暴力计算sums花的时间复杂度(一共 2 ⋅ 2 n 2 2 \cdot 2^{\frac{n}{2}} 222n)大。

class Solution {
public:vector<int> make_sums(span<int> nums){int n = nums.size();vector<int> sums(1ULL << n, 0);for (int i = 0; i < n; i ++ ){auto cur_bit = 1ULL << i;for (auto sub_mask = 0; sub_mask < cur_bit; sub_mask ++ ){sums[cur_bit | sub_mask] = sums[sub_mask] + nums[i];}}sort(sums.begin(), sums.end());return sums;}int find_ans(span<int> sums, int goal){int l = 0, r = sums.size() - 1;while (l < r){int mid = l + r + 1 >> 1;if (sums[mid] <= goal) l = mid;else r = mid - 1;}int res = abs(sums[l] - goal);if (l + 1 < sums.size()) res = min(res, abs(sums[l + 1] - goal));return res;}int minAbsDifference(vector<int>& nums, int goal) {int n = nums.size();span<int> fullspan(nums);int ln = n >> 1;auto lsums = make_sums(fullspan.subspan(0, ln));int rn = n - ln;auto rsums = make_sums(fullspan.subspan(ln, rn));int res = min(find_ans(lsums, goal), find_ans(rsums, goal));int i = 0, j = rsums.size() - 1;while (i < lsums.size() && j >= 0){int val = lsums[i] + rsums[j];res = min(res, abs(val - goal));if (val < goal) i ++ ;else if (val == goal) return 0;else j -- ;}return res;}
};

http://www.ppmy.cn/server/105534.html

相关文章

二叉树的分层遍历、栈的压入弹出序列

本章主要来讲解两个OJ题&#xff0c;针对每个OJ题我分三部分来解决&#xff0c;分别是题目解析&#xff08;主要弄清楚题目要求我们解决什么问题&#xff09;&#xff0c;算法原理&#xff0c;代码编写&#xff0c;接下来让我们进入正题。 一、二叉树的分层遍历 1.题目解析 题…

iOS profiles文件过期如何更新

创建发布用的Certificates 首先进入到https://developer.apple.com/account页面选择【证书】进入【新建证书】页面 点击【新建证书】按钮&#xff1a; 根据需求选中对应的【证书类型】&#xff0c;我选的是【Apple Distribution】&#xff0c; 开发者证书选择【Apple Devel…

React学习笔记(一)——react基础

目录 1. React 介绍 1.1 React是什么 1.2 React的优势 1.3 React的市场情况 2. 开发环境搭建 2.1 使用create-react-app快速搭建开发环境 2.2 react 项目文件说明 2.3 index.js项目入口文件 2.4 App.js 项目根组件 2.5 react 调试工具安装 3. JSX基础-概念和本质 3…

web 3D可视化技术

一.介绍 web 3D可视化技术的发展与应用展开&#xff0c;学习web 3D技术&#xff0c;包括利用js库进行项目搭建&#xff0c;学习图形学知识&#xff0c;掌握web 3D基础概念如点、线、面等&#xff0c;以及深入探讨渲染技术如PBR&#xff0c;材质贴图和环境光等。内容还涉及了与…

npm install 报错解决记录

引言 在使用 Node.js 和 npm&#xff08;Node Package Manager&#xff09;进行项目开发的过程中&#xff0c;经常会遇到 npm install 命令执行失败的情况。本文将总结一些常见的错误类型及其解决方案&#xff0c;帮助你在遇到这些问题时能够快速定位并解决问题。 1. 错误类型…

即时通讯IM软件推荐:五款适合企业内部使用的IM即时通讯软件

随着企业的不断发展&#xff0c;内部沟通和协作变得尤为重要。为了提高沟通效率、加强团队协作以及促进信息共享&#xff0c;企业需要选择适合自身需求的即时通讯IM软件。本文将为大家推荐五款适合企业内部使用的IM即时通讯软件&#xff0c;其中包括了备受赞誉的WorkPlus。 Wor…

operlayers绘制点,线,面,以及其他基本操作

绘制点 let coordinate [lon,lat] // 点 let point new ol.Feature({geometry: new ol.geom.Point(coordinate), }) // 样式 let style new ol.style.Style({// 边线颜色stroke: new ol.style.Stroke({color: #ffcc33,width: 2,}),// 字体样式text: new ol.style.Text({font…

模型 空雨伞

列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。观察现状&#xff0c;分析原因&#xff0c;制定行动。 1 空雨伞模型的应用 1.1 空雨伞模型应用之API对接的决策 某公司产品经理A君接到了与合作方对接API的任务。合作方对公司的中台API有特定的需求&…