当前位置: 首页 > news >正文

每日算法刷题Day80:10.27:leetcode 回溯11道题,用时3h

分享丨【算法题单】链表、二叉树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA/一般树) - 讨论 - 力扣(LeetCode)
本质是搜索树上的 DFS。
区分两个变量:需回溯的全局变量(内存占用少)和无需回溯的函数参数变量

一、入门回溯

1.套路
2.题目描述
3.学习经验
1. 17.电话号码的字母组合(中等)

17. 电话号码的字母组合 - 力扣(LeetCode)

思想

1.给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

代码
class Solution {
public:int n;vector<string> res;vector<string> tmp = {"",    "",    "abc",  "def", "ghi","jkl", "mno", "pqrs", "tuv", "wxyz"};void dfs(int id, string cur, string& digits) {if (id == n) {res.push_back(cur);return;}string str = tmp[digits[id] - '0'];for (char& c : str) {dfs(id + 1, cur + c, digits);}}vector<string> letterCombinations(string digits) {n = digits.size();dfs(0, "", digits);return res;}
};

二、子集型回溯

1.套路

1.有「选或不选」(好理解)和「枚举选哪个」两种写法。
也可以用二进制枚举做。
2.代码:

class Solution {
public:int maxSum = INT_MIN;int res = 0; // 答案int curSum = 0; // 过程值int n;void dfs(int id, vector<int>& nums, bool hasChosen) {if (id == n) { // 搜索完if (!hasChosen)return; // 空集if (curSum > maxSum) {maxSum = curSum;res = 1;} else if (curSum == maxSum)++res;return;}// 不选dfs(id + 1, nums, hasChosen);// 选// ...(判断是否能选)int tmp = curSum;curSum |= nums[id];dfs(id + 1, nums, true);curSum = tmp; // 回溯}int countMaxOrSubsets(vector<int>& nums) {// ...(预处理nums数组)n = nums.size();dfs(0, nums, false);return res;}
};
class Solution {
public:int res = 0;vector<bool> vis;int n;// 预处理输入数组bool hasChong(string str) {vector<bool> used(26, false);for (auto& c : str) {if (used[c - 'a'])return true;used[c - 'a'] = true;}return false;}void dfs(int id, int len, vector<string>& arr) {res = max(res, len); // 更新答案if (id == n) // 递归结束return;// 不选dfs(id + 1, len, arr);// 判断是否能选string& str = arr[id];bool isChosen = true;for (char& c : str) {if (vis[c - 'a']) {isChosen = false;break;}}// 能选if (isChosen) {for (char& c : str) {vis[c - 'a'] = true;}dfs(id + 1, len + str.size(), arr);for (char& c : str) { // 回溯vis[c - 'a'] = false;}}}int maxLength(vector<string>& arr) {vis.resize(26, false);vector<string> clearArr;for (auto& str : arr) { // 预处理输入数组if (!hasChong(str)) {clearArr.push_back(str);}}n = clearArr.size();dfs(0, 0, clearArr);return res;}
};
2.题目描述
3.学习经验
1. 子集(中等)

78. 子集 - 力扣(LeetCode)

思想

1.给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

代码
class Solution {
public:int n;vector<vector<int>> res;vector<int> cur;void dfs(int id, vector<int>& nums) {if (id == n) {res.push_back(cur);return;}// 不选dfs(id + 1, nums);// 选cur.push_back(nums[id]);dfs(id + 1, nums);cur.pop_back();}vector<vector<int>> subsets(vector<int>& nums) {n = nums.size();dfs(0, nums);return res;}
};
2, 1863.找出所有子集的异或总和再求和(简单)

1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)

思想

1.一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为  ,则异或总和为 0 。

  • 例如,数组 [2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1 。
    给你一个数组 nums ,请你求出 nums 中每个 子集 的 异或总和 ,计算并返回这些值相加之  。
    **注意:**在本题中,元素 相同 的不同子集应 多次 计数。
    数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a 。
代码
class Solution {
public:int n;int res = 0;int curSum = 0;void dfs(int id, vector<int>& nums) {if (id == n) {res += curSum;return;}dfs(id + 1, nums);curSum ^= nums[id];dfs(id + 1, nums);curSum ^= nums[id];}int subsetXORSum(vector<int>& nums) {n = nums.size();dfs(0, nums);return res;}
};
3. 784.字母大小写全排列(中等)

784. 字母大小写全排列 - 力扣(LeetCode)

思想

1.给定一个字符串 s ,通过将字符串 s 中的每个字母转变大小写,我们可以获得一个新的字符串。
返回 所有可能得到的字符串集合 。以 任意顺序 返回输出。

代码
class Solution {
public:int n;string cur;vector<string> res;void dfs(int id, string& s) {if (id == n) {res.push_back(cur);return;}dfs(id + 1, s);if (s[id] >= 'a' && s[id] <= 'z') {cur[id] = s[id] - 'a' + 'A';dfs(id + 1, s);cur[id] = s[id];} else if (s[id] >= 'A' && s[id] <= 'Z') {cur[id] = s[id] - 'A' + 'a';dfs(id + 1, s);cur[id] = s[id];}}vector<string> letterCasePermutation(string s) {n = s.size();cur = s;dfs(0, s);return res;}
};
4. 3566.等积子集的划分方案(中等)

3566. 等积子集的划分方案 - 力扣(LeetCode)

思想

1.给你一个整数数组 nums,其中包含的正整数 互不相同 ,另给你一个整数 target
请判断是否可以将 nums 分成两个 非空互不相交 的 子集 ,并且每个元素必须  恰好 属于 一个 子集,使得这两个子集中元素的乘积都等于 target
如果存在这样的划分,返回 true;否则,返回 false
子集 是数组中元素的一个选择集合。

代码

回溯一个集合:

class Solution {
public:typedef unsigned long long ull;ull curSum = 1, totalSum = 1;int n;bool res;void dfs(int id, vector<int>& nums, ull target) {if (res)return;if (id == n) {if (curSum == target && curSum * target == totalSum) {res = true;}return;}dfs(id + 1, nums, target); // 会为空if (curSum * nums[id] <= target) {curSum *= nums[id];dfs(id + 1, nums, target);curSum /= nums[id];}}bool checkEqualPartitions(vector<int>& nums, long long target) {n = nums.size();for (auto& x : nums)totalSum *= x;if (1ULL * target * target != totalSum)return false;dfs(0, nums, (ull)target);return res;}
};

递归两个集合(更易理解):

class Solution {
public:typedef unsigned long long ull;int n;bool res;void dfs(int id, vector<int>& nums, ull target, ull sum1, ull sum2) {if (res) // 剪枝return;if (id == n) {if (sum1 == target && sum2 == target) {res = true;}return;}if (sum1 * nums[id] <= target) // 剪枝dfs(id + 1, nums, target, sum1 * nums[id], sum2);if (sum2 * nums[id] <= target)dfs(id + 1, nums, target, sum1, sum2 * nums[id]);}bool checkEqualPartitions(vector<int>& nums, long long target) {n = nums.size();dfs(0, nums, (ull)target, 1, 1);return res;}
};
5. 2044.统计按位或能得到最大值的子集数目(中等)

2044. 统计按位或能得到最大值的子集数目 - 力扣(LeetCode)

思想

1.给你一个整数数组 nums ,请你找出 nums 子集 按位或 可能得到的 最大值 ,并返回按位或能得到最大值的 不同非空子集的数目 。
如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为数组 a 是数组 b 的一个 子集 。如果选中的元素下标位置不一样,则认为两个子集 不同 。
对数组 a 执行 按位或 ,结果等于 a[0] **OR** a[1] **OR** ... **OR** a[a.length - 1](下标从 0 开始)。

代码

用一个bool变量排除空集

class Solution {
public:int maxSum = INT_MIN;int res = 0;int curSum = 0;int n;void dfs(int id, vector<int>& nums, bool hasChosen) {if (id == n) {if (!hasChosen)return; // 空集if (curSum > maxSum) {maxSum = curSum;res = 1;} else if (curSum == maxSum)++res;return;}dfs(id + 1, nums, hasChosen);int tmp = curSum;curSum |= nums[id];dfs(id + 1, nums, true);curSum = tmp;}int countMaxOrSubsets(vector<int>& nums) {n = nums.size();dfs(0, nums, false);return res;}
};
6. LCP 51. 烹饪料理(简单)

LCP 51. 烹饪料理 - 力扣(LeetCode)

思想

1.欢迎各位勇者来到力扣城,城内设有烹饪锅供勇者制作料理,为自己恢复状态。
勇者背包内共有编号为 0 ~ 4 的五种食材,其中 materials[j] 表示第 j 种食材的数量。通过这些食材可以制作若干料理,cookbooks[i][j] 表示制作第 i 种料理需要第 j 种食材的数量,而 attribute[i] = [x,y] 表示第 i 道料理的美味度 x 和饱腹感 y
在饱腹感不小于 limit 的情况下,请返回勇者可获得的最大美味度。如果无法满足饱腹感要求,则返回 -1

代码
class Solution {
public:int n, m;int res = -1;vector<int> newMat; // 搜索中需回溯的变量void dfs(int id, vector<vector<int>>& cookbooks,vector<vector<int>>& attribute, int& limit, int meiSum,int baoSum) {if (baoSum >= limit)res = max(res, meiSum);if (id == n)return;dfs(id + 1, cookbooks, attribute, limit, meiSum, baoSum);auto& curCookBook = cookbooks[id];auto& curAttri = attribute[id];bool isCook = true;for (int j = 0; j < m; ++j) {if (curCookBook[j] > newMat[j]) {isCook = false;break;}}if (isCook) {for (int j = 0; j < m; ++j)newMat[j] -= curCookBook[j];dfs(id + 1, cookbooks, attribute, limit, meiSum + curAttri[0],baoSum + curAttri[1]);for (int j = 0; j < m; ++j)newMat[j] += curCookBook[j];}}int perfectMenu(vector<int>& materials, vector<vector<int>>& cookbooks,vector<vector<int>>& attribute, int limit) {n = cookbooks.size();m = materials.size();newMat = materials;dfs(0, cookbooks, attribute, limit, 0, 0);return res;}
};
7. 2397. 被列覆盖的最多行数(中等)

2397. 被列覆盖的最多行数 - 力扣(LeetCode)

思想

1.给你一个下标从 0 开始、大小为 m x n 的二进制矩阵 matrix ;另给你一个整数 numSelect,表示你必须从 matrix 中选择的 不同 列的数量。
如果一行中所有的 1 都被你选中的列所覆盖,则认为这一行被 覆盖 了。
形式上,假设 s = {c1, c2, ...., cnumSelect} 是你选择的列的集合。对于矩阵中的某一行 row ,如果满足下述条件,则认为这一行被集合 s 覆盖

  • 对于满足 matrix[row][col] == 1 的每个单元格 matrix[row][col]0 <= col <= n - 1),col 均存在于 s 中,或者
  • row 中 不存在 值为 1 的单元格。
    你需要从矩阵中选出 numSelect 个列,使集合覆盖的行数最大化。
    返回一个整数,表示可以由 numSelect 列构成的集合 覆盖 的 最大行数 。
代码
class Solution {
public:int n, m;vector<int> rowSum1;int res = 0;// [id,m):m-idvoid dfs(int id, int shengSelect, int sum, vector<vector<int>>& matrix) {if (m - id < shengSelect)return;if (shengSelect == 0) {res = max(res, sum);return;}dfs(id + 1, shengSelect, sum, matrix);for (int i = 0; i < n; ++i) {if (matrix[i][id] == 1) {--rowSum1[i];if (rowSum1[i] == 0)++sum;}}dfs(id + 1, shengSelect - 1, sum, matrix);for (int i = 0; i < n; ++i) {if (matrix[i][id] == 1) {++rowSum1[i];}}}int maximumRows(vector<vector<int>>& matrix, int numSelect) {n = matrix.size(), m = matrix[0].size();rowSum1.resize(n, 0);for (int i = 0; i < n; ++i) {for (int j = 0; j < m; ++j) {if (matrix[i][j] == 1)++rowSum1[i];}}int initSum = 0;for (int i = 0; i < n; ++i) {if (rowSum1[i] == 0)++initSum;}dfs(0, numSelect, initSum, matrix);return res;}
};
8. 1239. 串联字符串的最大长度(中等,对输入数组预处理进行优化)

1239. 串联字符串的最大长度 - 力扣(LeetCode)

思想

1.给定一个字符串数组 arr,字符串 s 是将 arr 的含有 不同字母 的 子序列 字符串 连接 所得的字符串。
请返回所有可行解 s 中最长长度。
子序列 是一种可以从另一个数组派生而来的数组,通过删除某些元素或不删除元素而不改变其余元素的顺序。
2.输入字符串数组中某个字符串可能会出现重复元素,原来逻辑只能判断两个字符串间是否会出现重复元素,可以考虑创建临时vis数组,显式判断,但会多次重复判断,所以最好的是预处理输入数组,删除有重复元素的字符串

代码
class Solution {
public:int res = 0;vector<bool> vis;int n;void dfs(int id, int len, vector<string>& arr) {if (id == n) {res = max(res, len);return;}dfs(id + 1, len, arr);string& str = arr[id];bool isChosen = true;vector<bool> tmp = vis;for (char& c : str) {if (tmp[c - 'a']) {isChosen = false;break;}tmp[c - 'a'] = true;}if (isChosen) {for (char& c : str) {vis[c - 'a'] = true;}dfs(id + 1, len + str.size(), arr);for (char& c : str) {vis[c - 'a'] = false;}}}int maxLength(vector<string>& arr) {n = arr.size();vis.resize(26, false);dfs(0, 0, arr);return res;}
};

输入数组预处理优化:

class Solution {
public:int res = 0;vector<bool> vis;int n;bool hasChong(string str) {vector<bool> used(26, false);for (auto& c : str) {if (used[c - 'a'])return true;used[c - 'a'] = true;}return false;}void dfs(int id, int len, vector<string>& arr) {res = max(res, len);if (id == n)return;dfs(id + 1, len, arr);string& str = arr[id];bool isChosen = true;for (char& c : str) {if (vis[c - 'a']) {isChosen = false;break;}}if (isChosen) {for (char& c : str) {vis[c - 'a'] = true;}dfs(id + 1, len + str.size(), arr);for (char& c : str) {vis[c - 'a'] = false;}}}int maxLength(vector<string>& arr) {vis.resize(26, false);vector<string> clearArr;for (auto& str : arr) {if (!hasChong(str)) {clearArr.push_back(str);}}n = clearArr.size();dfs(0, 0, clearArr);return res;}
};

三、划分型回溯

1.套路

1.把分割线(逗号)看成是可以「选或不选」的东西,本质是子集型回溯

2.题目描述
3.学习经验
1. 131.分割回文串(中等,学习优化)

131. 分割回文串 - 力扣(LeetCode)

思想

1.给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
2.我的代码有个变量cur,表示当前待分割的字符串,但是递归拷贝开销大,可以用一个start变量代替,表示当前待分割的字符串的起始位置,[start,id]就是待分割字符串,用substr实现

代码
class Solution {
public:int n;vector<vector<string>> res;vector<string> tmp;bool isHui(string str) {int len = str.size();for (int i = 0, j = len - 1; i <= j; ++i, --j) {if (str[i] != str[j])return false;}return true;}void dfs(int id, string s, string cur) {if (id == n) {if (cur == "") // 全部分割完res.push_back(tmp);return;}cur += s[id];dfs(id + 1, s, cur);if (isHui(cur)) {tmp.push_back(cur);cur = "";dfs(id + 1, s, cur);tmp.pop_back();}}vector<vector<string>> partition(string s) {n = s.size();dfs(0, s, "");return res;}
};

优化代码:

class Solution {
public:int n;vector<vector<string>> res;vector<string> tmp;bool isHui(string& s, int left, int right) {while (left < right) {if (s[left++] != s[right--])return false;}return true;}void dfs(int id, string& s, int start) {if (id == n) {if (start == n) // 全部分割完res.push_back(tmp);return;}if (id < n - 1)dfs(id + 1, s, start); // 最后一个必分割// [start,id]if (isHui(s, start, id)) {tmp.push_back(s.substr(start, id - start + 1));dfs(id + 1, s, id + 1); // start更新tmp.pop_back();}}vector<vector<string>> partition(string s) {n = s.size();dfs(0, s, 0);return res;}
};
2. 2698. 求一个整数的惩罚数(中等)

2698. 求一个整数的惩罚数 - 力扣(LeetCode)

思想

1.给你一个正整数 n ,请你返回 n 的 惩罚数 。
n 的 惩罚数 定义为所有满足以下条件 i 的数的平方和:

  • 1 <= i <= n
  • i * i 的十进制表示的字符串可以分割成若干连续子字符串,且这些子字符串对应的整数值之和等于 i 。
代码
class Solution {
public:bool tag;int len;int target;int sum;void dfs(int id, string& s, int start) {if (tag || sum > target)return;if (id == len) {if (start == len) {if (sum == target)tag = true;}return;}if (id < len - 1)dfs(id + 1, s, start);// [start,id]int val = 0;for(int i=start;i<=id;++i){val=val*10+(s[i]-'0');}sum += val;dfs(id + 1, s, id + 1);sum -= val;}bool isCheng(int x) {tag = false;string s = to_string(x * x);target = x;sum = 0;len = s.size();dfs(0, s, 0);return tag;}int punishmentNumber(int n) {int res = 0;for (int i = 1; i <= n; ++i) {if (isCheng(i))res += i * i;}return res;}
};
http://www.dtcms.com/a/536927.html

相关文章:

  • 建设一个网站的规划广州seo公司如何
  • [强化学习] 第1篇:奖励信号是智能的灵魂
  • 从“看得见“到“看得懂“:监控安全管理的智能进化
  • YOLOv5 核心模块解析与可视化
  • 昆山外贸型网站制作建站科技公司
  • 快速建站框架网站如何做360优化
  • 网站公司做网站网络推广公司介绍
  • 百度网站验证方法室内设计效果图多少钱一张
  • 网站服务器查找wordpress cms主题制作
  • 《Chart.js 柱形图:全面解析与实战指南》
  • 物联网设备运维中的上下文感知自动化响应与策略动态调整
  • JAVA面试汇总(五)数据库(二)
  • 程序员的自我修养(三)
  • 【C++】--list的使用和模拟实现
  • Windows 11 AI原生转型:代理式工作流的核心技术与模块化架构实践
  • 网站前台设计方案企业vi设计书籍
  • 建设读书网站的意义免费ppt模板在哪里下载
  • C++2D地铁跑酷代码
  • 库室安防设施架构-自主可控、支持国产化
  • 站长工具之家百度权重4网站值多少钱
  • Vue3 计算属性与监听器:computed、watch、watchEffect 用法解析
  • 题解:P14307 【MX-J27-T4】点灯
  • 网站关键词一般设置几个北京一家专门做会所的网站
  • 语文建设投稿网站wordpress静态cdn
  • 精品数据分享 | 锂电池数据集(一)新能源汽车大规模锂离子电池数据集
  • 01.LLM的背景知识
  • 17-21自增,自减,逻辑运算符,非布尔值的与或非,赋值运算符
  • 感兴趣可以看看使用xtrabackup 备份与恢复MySQL数据完整操作过程
  • 数据库安装卸载及作业
  • termux下python编程尝试,转换全能扫描王生成pdf文件