Leetcode | 40 组合总和II

news2024/11/25 4:57:06

40 组合总和II

文章目录

  • 40 组合总和II
    • 题目
    • 官方解法:回溯
      • 思路与算法
    • code
    • Reference

题目

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用 一次

**注意:**解集不能包含重复的组合。

示例 1:

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

示例 2:

输入: candidates = [2,5,2,1,2], target = 5,
输出:
[
[1,2,2],
[5]
]

提示:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

官方解法:回溯

思路与算法

由于我们需要求出所有和为 target的组合,并且每个数只能使用一次,因此我们可以使用递归 + 回溯的方法来解决这个问题:

  • 我们用 dfs(pos,rest) 表示递归的函数,其中 pos表示我们当前递归到了数组 candidates中的第 pos\textit{pos}pos 个数,而 rest表示我们还需要选择和为 rest 的数放入列表作为一个组合;

  • 对于当前的第 pos个数,我们有两种方法:选或者不选。如果我们选了这个数,那么我们调用 dfs(pos+1,rest−candidates[pos]) 进行递归,注意这里必须满足 rest≥candidates[pos]。如果我们不选这个数,那么我们调用 dfs(pos+1,rest)进行递归;

  • 在某次递归开始前,如果 rest 的值为 0,说明我们找到了一个和为 target的组合,将其放入答案中。每次调用递归函数前,如果我们选了那个数,就需要将其放入列表的末尾,该列表中存储了我们选的所有数。在回溯时,如果我们选了那个数,就要将其从列表的末尾删除。

上述算法就是一个标准的递归 + 回溯算法,但是它并不适用于本题。这是因为题目描述中规定了解集不能包含重复的组合,而上述的算法中并没有去除重复的组合。

例如当 candidates=[2,2],target=2 时,上述算法会将列表 [2][2][2] 放入答案两次。

因此,我们需要改进上述算法,在求出组合的过程中就进行去重的操作。我们可以考虑将相同的数放在一起进行处理,也就是说,如果数 x 出现了 y 次,那么在递归时一次性地处理它们,即分别调用选择 0,1,⋯ ,y次 x的递归函数。这样我们就不会得到重复的组合。具体地:

  • 我们使用一个哈希映射(HashMap)统计数组 candidates 中每个数出现的次数。在统计完成之后,我们将结果放入一个列表 freq中,方便后续的递归使用。
    • 列表 freq 的长度即为数组 candidates 中不同数的个数。其中的每一项对应着哈希映射中的一个键值对,即某个数以及它出现的次数。
  • 在递归时,对于当前的第 pos 个数,它的值为 freq[pos][0],出现的次数为 freq[pos][1],那么我们可以调用

dfs(pos+1,rest−i×freq[pos][0])
即我们选择了这个数 iii 次。这里 iii 不能大于这个数出现的次数,并且i×freq[pos][0]也不能大于 rest\textit{rest}rest。同时,我们需要将 i个 freq[pos][0]放入列表中。

这样一来,我们就可以不重复地枚举所有的组合了。

我们还可以进行什么优化(剪枝)呢?一种比较常用的优化方法是,我们将 freq根据数从小到大排序,这样我们在递归时会先选择小的数,再选择大的数。这样做的好处是,当我们递归到 dfs(pos,rest) 时,如果freq[pos][0]已经大于 rest,那么后面还没有递归到的数也都大于 rest,这就说明不可能再选择若干个和为 rest的数放入列表了。此时,我们就可以直接回溯。

code

class Solution {
private:
    vector<pair<int,int>> freq;
    vector<int> sequence;
    vector<vector<int>> ret;
public:

    void dfs(int pos, int rest) {
        if(rest == 0) {
            ret.emplace_back(sequence);
            return;
        }

        if(pos == freq.size() || rest < freq[pos].first) {
            return;
        }

        dfs(pos + 1, rest);

        int most = min(rest / freq[pos].first, freq[pos].second);
        for (int i  = 1; i <= most; ++i) {
            sequence.emplace_back(freq[pos].first);
            dfs(pos + 1, rest - i * freq[pos].first);
        }

        for (int i  = 1; i <= most; ++i) {
            sequence.pop_back();
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        std::sort(candidates.begin(), candidates.end());
        for(auto &num: candidates) {
            if(freq.empty() || num != freq.back().first) {
                freq.emplace_back(num, 1);
            } else {
                ++freq.back().second;
            }
        }
        dfs(0, target);
        return ret;
    }
};

最核心的还是对于重复的数字只使用1次这个思路,就是首先排序,然后分析该数字能够使用的最多的次数,不仅要判断 rest 中最多包含几个重复数字,也要判断重复数字的个数。因此首先就得对candidate数组进行排序。

int most = min(rest / freq[pos].first, freq[pos].second);
for (int i  = 1; i <= most; ++i) {
    sequence.emplace_back(freq[pos].first);
    dfs(pos + 1, rest - i * freq[pos].first);
}

for (int i  = 1; i <= most; ++i) {
    sequence.pop_back();
}

Reference

  • 组合总和 II
  • 力扣官方题解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/644332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【哈佛积极心理学笔记】第12讲 写日记

第12讲 写日记 preparationincubation (take a break, need some rest time)evaluationelaboration JP Morgan’s quote, I can do a year’s work in 9 months but not in 12" a simple technique of intervation: journaling Postive emotions and painful emotions…

CSS基础学习--12 分组 和 嵌套 选择器

一、分组选择器 在样式表中有很多具有相同样式的元素 h1 {color:green; } h2 {color:green; } p {color:green; } 为了尽量减少代码&#xff0c;你可以使用分组选择器。 每个选择器用逗号分隔。 在下面的例子中&#xff0c;我们对以上代码使用分组选择器&#xff1a; <!DO…

windows下cmake的小白级入门使用教程(hello world)

想学习cmake&#xff0c;基于惯性思维&#xff0c;想先跑通一个“hello world”的例子&#xff0c;奈何网上教程一大把&#xff0c;有用的教程破费功夫寻找。大部分教程都没有从新电脑(重装系统后的电脑)的角度讲述步骤。 为了得到干净的电脑环境&#xff0c;研究了一段时间VMw…

【图像任务】Transformer系列.3

本文介绍3篇改进Transformer以实现不同图像任务的工作&#xff1a;少样本医学图像分割CAT-Net&#xff08;arXiv2023&#xff09;&#xff0c;高效图像重建等任务GRL&#xff08;CVPR2023&#xff09;&#xff0c;轻量视觉Transformer中的局部信息思考CloFormer&#xff08;arX…

根据指定条件和规则逐一判断两个数组中对应元素是否接近 numpy.isclose()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 根据指定条件和规则逐一判断 两个数组中对应元素是否接近 numpy.isclose() [太阳]选择题 请问关于以下代码的表述错误的是&#xff1f; import numpy as np a np.array([2, 7, np.nan]) b …

【RabbitMQ教程】前言 —— 消息队列介绍

&#x1f4a7; 【 R a b b i t M Q 教程】前言——消息队列介绍 \color{#FF1493}{【RabbitMQ教程】前言 —— 消息队列介绍} 【RabbitMQ教程】前言——消息队列介绍&#x1f4a7; &#x1f337; 仰望天空&#xff0c;妳我亦是行人.✨ &#x1f984; 个人主页——微风…

力扣题库刷题笔记3--无重复字符的最长子串

1、题目如下&#xff1a; 2、个人Python代码实现如下&#xff1a; 代码如下&#xff1a; class Solution: def lengthOfLongestSubstring(self, s: str) -> int: temp "" #临时变量&#xff0c;记录当前连续不重复子串 out_put …

MEC | 条款1 仔细区别pointers和references

More Effective C&#xff08;MEC&#xff09; 文章目录 More Effective C&#xff08;MEC&#xff09;条款1 仔细区别pointers和references结论 本章描述 pointers 和 references 的差异&#xff0c;并告诉你它们适当使用时机。 条款1 仔细区别pointers和references pointers…

cxgrid显示海量数据

在默认情况下&#xff0c;cxgrid显示几万条以上的数据会很慢。怎么办&#xff1f; 交下面的属性设为TRUE以后&#xff0c;速度飞快。 但速度是快了&#xff0c;自动计算列的合计值这些功能却失效了&#xff0c;正所谓有得必有失&#xff01;

CSS基础学习--11 padding(填充)

一、定义 CSS padding&#xff08;填充&#xff09;是一个简写属性&#xff0c;定义元素边框与元素内容之间的空间&#xff0c;即上下左右的内边距。 当元素的 padding&#xff08;填充&#xff09;内边距被清除时&#xff0c;所释放的区域将会受到元素背景颜色的填充。 单独使…

软考A计划-系统架构师-案例分析考前背诵-下篇

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff…

XSS注入(跨站脚本攻击)

今天学习一下xss注入 1.XSS是什么 XSS注入漏洞又称为"跨站脚本攻击(Cross Site Scripting)"&#xff0c;为了不和层叠样式表(Cascading Style Sheets,CSS)混淆&#xff0c;所以将跨站脚本攻击缩写为XSS。xss本质上是黑客通过对网页的HTML注入&#xff0c;篡改了原本…

C\C++ Thread-多线程

文章作者&#xff1a;里海 来源网站&#xff1a;https://blog.csdn.net/WangPaiFeiXingYuan 简介 c多线程 时间 c语言的时间处理&#xff1a;time.h 获取从1970年1月1日到当前经过的秒数: long t0 time(NULL); 让程序暂停3秒&#xff1a; sleep(3); 当前时间的3秒后&…

基于Java购物商城系统设计与实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

Linux 环境下Docker部署项目(七)

文章目录 一、前言二、项目打包上传1. 项目打包简单 (故省略)2. 上传jar包程序到Linux 三、拉取镜像1. 拉取镜像java:8(jdk1.8)2. 拉取 java8 出错&#xff1a; 四、创建Dockerfile文件1. 创建Dockerfile文件2. 编辑Dockerfile3. 构建镜像&#xff08;需要在Dockerfile同级目录…

腾讯安全发布“数字安全免疫力”模型框架,建设发展驱动安全新范式

随着数字化进程加快&#xff0c;企业数字化体系的边界在不断拓展&#xff0c;安全风险和挑战不断增加&#xff0c;传统被动防御的安全应对常显疲态&#xff0c;数字安全时代亟待建立全新的安全范式。 6月13日&#xff0c;腾讯安全联合IDC在北京发布“数字安全免疫力”模型框架…

Nucleo-F411RE (STM32F411)LL库体验 1 - 点亮LED

Nucleo-F411RE &#xff08;STM32F411&#xff09;LL库体验 1 - 点亮LED 1、开发环境 一如既往&#xff0c;还是macos gcc pyocd的开发环境。 pyocd pack find stm32f411retx #命令查找支持包 pyocd pack install stm32f411retx #安装支持包 pyocd flash --erase auto --ta…

跨站点脚本(XSS)的介绍

目录 1、概述 2、XSS普遍认可三种形式&#xff1a; ​2.1 反射式 XSS 攻击 2.2 存储的 XSS 攻击 2.3 基于DOM的XSS 攻击 2.3.1 DOM中相关建议&#xff1a; 2.3.2 利用javascript开发时的建议规则&#xff1a; 3、XSS 攻击后果 4、框架安全 5、XSS 防御理念 6、XSS P…

Linux(链接器的意义)

文章目录 前言一、链接器概念介绍二、目标文件三、main函数是第一个被执行的函数吗&#xff1f;四、链接脚本的意义和作用总结 前言 本篇文章我们来讲解链接器的意义。 一、链接器概念介绍 链接器&#xff08;Linker&#xff09;是计算机编译器系统中的一个重要组成部分&…

MongoDB安装、设置密码、操作命令、配置文件说明、备份与还原

目录 一、mongodb概述 二、mongodb安装部署 三、mongodb设置密码 四、MongoDB操作命令与说明 五、配置文件说明 六、备份与还原 一、mongodb概述 MongoDB是一个非关系型数据库管理系统&#xff0c;它使用文档模型存储数据。MongoDB中的文档类似于JSON对象&#xff0c;可以…