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

题目描述

给一个整数数组nums,数组长度在40以下
再给一个目标值goal
问怎么能在数组中选一个子序列,让子序列的和最接近值goal?

f1-折半+状态压缩+动态规划

基本分析

1.40这个长度怎么看,直接用状态压缩行不行?子集个数是O(2n), 每个子集需要O(n)去找一个子集中的非0元素,总复杂度是O(n2n),对40这个长度,明显不行
2.怎么缩减规模?分治思想,将原来数组划分为左半和右半,分别枚举子集和lsum、rsum。原数组的一个子序列和,一定是3者之一

  • lsum某个元素
  • rsum的某个元素
  • lsum某个元素 与 rsum某个元素和
  1. 以上划分后怎么处理?
  • 直接遍历
  • 直接遍历
  • 抽象为经典双指针问题:给定两个数组,怎么在两个数组中各挑出一个整数,使它们的和尽可能接近目标值

代码

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分别要O(n2n/2), 排序要O(nlog(2n/2)), 双指针要O(2n/2)
空间:放枚举的子集和需要O(2n/2)

总结

题目转化:这个题的特点在于不用枚举子集,但是因为n=40,需要有分治的想法,把题目转化成常规的状压dp+双指针经典问题
双指针经典问题:注意要先排序。对l、r的范围注意是range(1<<ls), range(1<<rs),不是ls和rs。另外指针移动的策略是和>值,右指针向左,如果和<值,左指针向右

posted @   zhangk1988  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示