首页 > 编程语言 >代码随想录DAY23 - 回溯算法 - 08/22

代码随想录DAY23 - 回溯算法 - 08/22

时间:2024-08-22 21:55:48浏览次数:14  
标签:num target 22 int 08 随想录 vector candidates sum

组合总和

题干

题目:给你一个 无重复元素整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

说明:

  • 2 <= candidates[i] <= 40,数组中都是正数

  • 解集不能包含重复的组合

链接:. - 力扣(LeetCode)

思路和代码

同样是组合,但这道题不限制组合的大小,也不限制同一数字的使用次数。之前在做组合题目时,每用一个数字,下一个组合就不能用之前用过的,但在这道题里,每考虑一个新组合时本身这个数字能再次使用,这就是在原组合题目的基础上要修改的地方。

递归法
  • 递归参数和返回值:参数是目标和 target、组合之和 sum、传入的集合 candidates、起始的元素位置 startIndex;无返回值。

  • 递归结束的条件:当组合之和 sum 大于等于 目标和target时就要返回。

  • 递归顺序:先确定组合中的一个元素,再递归寻找下一个元素凑组合。但和之前不同的是,我们递归寻找下一个元素时还可以是自身,因为可以重复,因为递归时候传入的起始位置仍为 i,即 backTracking(target, sum, candidates, i),而不是 i+1。

class Solution {
public:
    vector<int> composition; // 记录组合
    vector<vector<int>> result; // 记录所有组合的结果
    void backTracking(int target, int &sum, vector<int>& candidates, int startIndex){
        if (sum == target){ // sum 记录组合之和
            result.push_back(composition);
            return;
        } else if (sum > target){
            return;
        }
        for (int i = startIndex; i < candidates.size(); ++i) {
            int num = candidates[i];
            composition.push_back(num);
            sum += num;
            backTracking(target,sum,candidates,i); // 这里输入应该是 i,而不是 i+1,因为元素可以重复
            composition.pop_back();
            sum -= num;
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        int sum = 0;
        backTracking(target,sum,candidates,0);
        return result;
    }
};
递归优化:剪枝

优化:对数组排序并减少不必要的 for 循环

数组 candidates 本身是无序的,因此我们在遍历到 sum > target 时返回后,又需要继续遍历下一个元素。但如果数组是升序排列,当 sum > target 时,也不需要往下再遍历了,因为之后的数组元素只会更大,不可能再出现 sum = target 的情况,可以直接终止循环。

class Solution {
public:
    vector<int> composition;
    vector<vector<int>> result;
    void backTracking(int target, int &sum, vector<int>& candidates, int startIndex){
        if (sum == target){
            result.push_back(composition);
            return;
        }
        for (int i = startIndex; i < candidates.size() && sum+candidates[i] <= target; ++i) {
        											// 剪枝操作,如果加入当前元素已经 > target 直接终止循环
            int num = candidates[i];
            composition.push_back(num);
            sum += num;
            backTracking(target,sum,candidates,i); 
            composition.pop_back();
            sum -= num;
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        int sum = 0;
        sort(candidates.begin(),candidates.end()); // 升序排列
        backTracking(target,sum,candidates,0);
        return result;
    }
};

组合总和Ⅱ

题干

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

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

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

说明:

  • 1 <= candidates[i] <= 50,数组中只有正数

  • 数组中可能有重复元素

链接:. - 力扣(LeetCode)

思路和代码

本题和上一题不一样的地方就在于数组 candidates 中可能包含重复的数字,而且每个数字在一次组合中只能使用一次,不可以多次使用。由于有重复的数字,这样递归求得的组合中可能会重复,因此我们需要先对 candidates 去重,重复出现的数字其实表明了该数字在组合中可以用多少次,我们需要记录下不同数字的使用次数。

递归法
class Solution {
public:
    int count[51]; // 记录不同数字的使用次数, 1 <= candidates[i] <= 50
    vector<int> composition; // 记录组合
    vector<vector<int>> result; // 记录所有组合的结果
    void backTracking(vector<int>& candidates, int target, int sum, int startIndex){
        if (sum == target){
            result.push_back(composition);
            return;
        } else if (sum > target){
            return;
        }
        for (int i = startIndex; i < candidates.size(); ++i) {
            int num = candidates[i];
            sum += num;
            composition.push_back(num);
            count[num]--; // 此时数字 num 已被使用一次,故次数减一
            if (count[num] > 0) backTracking(candidates,target,sum,i); // 若还有使用次数,则重复使用数字
            else backTracking(candidates,target,sum,i+1); // 若没有使用次数,则使用下一个数字
            composition.pop_back();
            sum -= num;
            count[num]++;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<int> newCandidates; // 记录去重后的新数组
        for (int i : candidates) {
            if (count[i] == 0){
                newCandidates.push_back(i);
            }
            count[i]++; // 记录下每个数字的使用次数
        }
        backTracking(newCandidates,target,0,0);
        return result;
    }
};
递归优化:剪枝

同样是对 candidates 数组进行升序排序,这样当 sum > target 时无需再进入循环。

class Solution {
public:
    int count[51]; // 1 <= candidates[i] <= 50
    vector<int> composition;
    vector<vector<int>> result;
    void backTracking(vector<int>& candidates, int target, int sum, int startIndex){
        if (sum == target){
            result.push_back(composition);
            return;
        }
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; ++i) {
            										// 剪枝,减少循环
            int num = candidates[i];
            sum += num;
            composition.push_back(num);
            count[num]--;
            if (count[num] > 0) backTracking(candidates,target,sum,i);
            else backTracking(candidates,target,sum,i+1);
            composition.pop_back();
            sum -= num;
            count[num]++;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<int> newCandidates; // 记录去重后的新数组
        for (int i : candidates) {
            if (count[i] == 0){
                newCandidates.push_back(i);
            }
            count[i]++; // 记录下每个数字的使用次数
        }
        sort(newCandidates.begin(),newCandidates.end()); // 排序
        backTracking(newCandidates,target,0,0);
        return result;
    }
};

标签:num,target,22,int,08,随想录,vector,candidates,sum
From: https://blog.csdn.net/chan_lay/article/details/141438877

相关文章

  • 代码随想录算法训练营第二十三天(回溯 二)
    力扣题部分:39.组合总和题目链接:.-力扣(LeetCode)题面:给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。candid......
  • 代码随想录算法训练营第二十二天(回溯 一)
    开始学习回溯!回溯理论基础代码随想录文章链接:代码随想录文章摘要:什么是回溯法回溯法也可以叫做回溯搜索法,它是一种搜索的方式。在二叉树系列中,我们已经不止一次,提到了回溯。回溯是递归的副产品,只要有递归就会有回溯。所以以下讲解中,回溯函数也就是递归函数,指的都是一......
  • YC327B [ 20240821 CQYC NOIP 模拟赛 T2 ] 括号串(bracket)
    题意给定\(S\in\{(,),?\}\)。定义深度为括号嵌套的子序列的最大长度除以\(2\)。求出将\(?\)替换为括号的所有括号串的深度之和,对\(998244353\)取模。\(n\le10^6\)。Sol考虑如何把每次贡献只计算一次。不难想到在括号的中心点计算。可以发现,若当前左右括号......
  • H7-TOOL脱机烧录的UID加密操作方法,支持一键生成目标板C代码,方便大家轻松操作(2024-08-2
    UID加密使用比较方便,对应的C代码模板已经做好,使用TOOL上位机生成后,直接复制粘贴到自己的工程即可使用。返回1表示解密成功,返回0表示失败。【UID加密原理】1、烧录器在烧录芯片时,按照指定的算法将UID码编码为一个加密数据,并写入FLASH指定区域。2、用户的程序必须增加一段UID校......
  • 免输密码全自动登录金山文档Windows客户端 2024年8月22日
     免输密码全自动登录金山文档Windows客户端2024年8月22日  ;免输密码全自动登录金山文档Windows客户端2024年8月22日;;指纹加密U盘&FindText-v9.7-by-FeiYue&Loop-if-break&金山文档&Index-Your-Files&mstsc&零层壹号&WinSCP&USMv5&Acronis-true-Image-2021-WinPE&......
  • Selenium + Python 自动化测试22(PO+数据驱动)
            我们的目标是:按照这一套资料学习下来,大家可以独立完成自动化测试的任务。上一篇我们讨论了PO模式和unittest框架结合起来使用。        本篇文章我们综合一下之前学习的内容,如先将PO模式、数据驱动思想和我们生成HTML报告融合起来,综合的灵活的使用......
  • 代码随想录Day23
    131.分割回文串给你一个字符串s,请你将s分割成一些子串,使每个子串都是回文串。返回s所有可能的分割方案。示例1:输入:s="aab"输出:[["a","a","b"],["aa","b"]]示例2:输入:s="a"输出:[["a"]]提示:1<=s.length<=16s仅由......
  • 「代码随想录算法训练营」第四十四天 | 图论 part2
    200.岛屿数量题目链接:https://leetcode.cn/problems/number-of-islands/description/文章讲解:https://programmercarl.com/kamacoder/0099.岛屿的数量深搜.html题目难度:中等题目状态:看题解思路一:深搜版方法dfs:参数:接受一个字符网格grid和当前坐标(r,c)。功能:......
  • 给自己复盘的随想录笔记-移除元素
    双指针法双指针法(快慢指针法): 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。定义快慢指针快指针:寻找新数组的元素,新数组就是不含有目标元素的数组慢指针:指向更新新数组下标的位置相关题目删除有序数组中的重复项解题思路:解法:双指针首先注意数组......
  • 给自己复盘用的随想录笔记day1-二分查找
    二分法使用情景数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件,当大家看到题目描述满足如上条件的时候,可要想一想是不是可以用二分法了。求某一个整数的平方根边界条件写二分法......