<数组中选取子集达到某一目标>问题总结

这类问题主要分为两种类型:

  • 目标值明确,可以把目标值看出背包容量,数组值看做物品,转成背包问题
  • 目标值不明确,容量不知道,不能用背包,只能枚举子集的和

类型一:

类型二:

Leetcode 1555

题目描述

给你一个整数数组 nums 和一个目标值 goal

你需要从 nums 中选出一个子序列,使子序列元素总和最接近 goal 。也就是说,如果子序列元素和为 sum ,你需要 最小化绝对差 abs(sum - goal)

返回 abs(sum - goal) 可能的 最小值

  • 1 <= nums.length <= 40
  • -107 <= nums[i] <= 107
  • -109 <= goal <= 109

解题思路

这道题最后要求的试和与目标之间的差距尽可能地小,所以其实目标是不确定的,属于类型2

再观察数据范围个数为40个,考虑把他分成两个数组处理,2的20次方复杂度刚好够

采用状态压缩的方式存储信息

采用双指针逼近目标值

#include<bits/stdc++.h>

using namespace std ;

int ans = 0x7fffffff ;
int nums[41],numsSize,goal ;
int lp[1<<20],rp[1<<20] ;

int main()
{
    scanf("%d",&numsSize) ;
    for(int i=1;i<=numsSize;++i) scanf("%d",&nums[i]) ;
    scanf("%d",&goal) ;

    int part = numsSize/2 ;
    int Rpart = numsSize-(part+1)+1 ;
    //L:1~part  R: part+1~numsSize ;
    for(int i=0;i<(1<<part);++i)
    {
        for(int j=1;j<=part;++j)
        {
            if((1&(i>>(j-1)))==0) continue ;
            lp[i]+=nums[j] ;
        }
        ans = min(ans,abs(goal-lp[i])) ;
    }
    
    for(int i=0;i<(1<<Rpart);++i)
    {
        for(int j=1+part;j<=numsSize;++j)
        {
            if((1&(i>>(j-1-part)))==0) continue ;
            rp[i]+=nums[j] ;
        }
        ans  = min(ans,abs(goal-rp[i])) ;
    }
    sort(lp,lp+(1<<part)) ;
    sort(rp,rp+(1<<Rpart)) ;
    
    int i=0,j=(1<<Rpart)-1 ;
    while(i<(1<<part)&&j>=0)
    {
        int sum = lp[i]+rp[j] ;
        ans = min(ans,abs(goal-sum)) ;
        if(sum<goal) i++ ;
        else if(sum>goal) j-- ;
        else break ;
    }
    printf("%d\n",ans) ;
    system("pause") ;
    return 0 ;
}

LuoguP4799

题目描述

给出一个数组,给定一个值x要求从数组中选出一系列数使得这些数的和小于x,求方案数。

解题思路

该题也属于从数组中找一系列子序列满足题目要求,题目要求的是方案书,也是不确定的,所以属于类型二。

注意到该数据范围N<40,考虑将数组折半双指针处理

#include<bits/stdc++.h>

using namespace std ;

int n  ;
long long  m ;
long long value[41] ;
long long Lsum[1<<20],Rsum[1<<20];

long long ans= 0;
int main()
{
    scanf("%d%lld", &n,&m) ;
    for(int i=1;i<=n;++i) scanf("%lld",&value[i]) ;
    int Lpart = n/2 ;
    int Rpart = n-Lpart ;
    for(int i=0;i<(1<<Lpart);++i)
    {
        for(int j=1;j<=Lpart;++j)
        {
            if(((i>>(j-1))&1)==0) continue ;
            Lsum[i]+=value[j] ;
        }
    }
    for(int i=0;i<(1<<Rpart);++i)
    {
        for(int j=Lpart+1;j<=n;++j)
        {
            if(((i>>(j-1-Lpart))&1)==0) continue ;
            Rsum[i]+=value[j] ;
        }
    }
    sort(Lsum,Lsum+(1<<Lpart)) ;
    sort(Rsum,Rsum+(1<<Rpart)) ;
    int i=0,j=(1<<Rpart)-1 ;
    for(;;)
    {
        for(;j>=0;j--)
        {
            if(Lsum[i]+Rsum[j]<=m) break ;
        }
        if(j>=0&&i<(1<<Lpart))
        {
            ans+=(j+1) ;
            i++ ;
            continue ;
        }
        else break ;
    }
    printf("%lld",ans) ;
    return 0 ;
}
posted @   Bo-Wing  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示