1755. 最接近目标值的子序列和
题目描述
给一个整数数组nums,数组长度在40以下
再给一个目标值goal
问怎么能在数组中选一个子序列,让子序列的和最接近值goal?
f1-折半+状态压缩+动态规划 |
基本分析
1.40这个长度怎么看,直接用状态压缩行不行?子集个数是, 每个子集需要O(n)去找一个子集中的非0元素,总复杂度是,对40这个长度,明显不行
2.怎么缩减规模?分治思想,将原来数组划分为左半和右半,分别枚举子集和lsum、rsum。原数组的一个子序列和,一定是3者之一
- lsum某个元素
- rsum的某个元素
- lsum某个元素 与 rsum某个元素和
- 以上划分后怎么处理?
- 直接遍历
- 直接遍历
- 抽象为经典双指针问题:给定两个数组,怎么在两个数组中各挑出一个整数,使它们的和尽可能接近目标值
代码
class Solution: def minAbsDifference(self, nums: List[int], goal: int) -> int: n = len(nums) harf = n//2 ls, rs = harf, n-harf # 求左半组合 lsum = [0] * (1<<ls) for i in range(1, 1<<ls): for j in range(ls): if i & (1<<j): lsum[i] = lsum[i-(1<<j)] + nums[j] break # 求右半组合 rsum = [0] * (1<<rs) for i in range(1, 1<<rs): for j in range(rs): if i & (1<<j): rsum[i] = rsum[i-(1<<j)] + nums[ls+j] break ans = inf for v in lsum: ans = ans if ans < abs(v-goal) else abs(v-goal) for v in rsum: ans = ans if ans < abs(v-goal) else abs(v-goal) lsum.sort() rsum.sort() # 求左右混合 l, r = 0, (1<<rs)-1 while l < (1<<ls) and r >= 0: s = lsum[l] + rsum[r] ans = min(ans, abs(s-goal)) if s > goal: r -= 1 elif s < goal: l += 1 else: break return ans
复杂度
时间:求解lsum及rsum分别要, 排序要, 双指针要
空间:放枚举的子集和需要
总结
题目转化:这个题的特点在于不用枚举子集,但是因为n=40,需要有分治的想法,把题目转化成常规的状压dp+双指针经典问题
双指针经典问题:注意要先排序。对l、r的范围注意是range(1<<ls), range(1<<rs),不是ls和rs。另外指针移动的策略是和>值,右指针向左,如果和<值,左指针向右
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现