回溯五题【Leetcode17数独/37组合问题/51N皇后/212字典树/980状态压缩】

文章目录

  • 关于回溯
  • 37. 解数独(37.sudoku-solver)
  • 17. 电话号码的数字组合(17.letter-combinations-of-a-phone-number)
  • 51. N皇后(51.n-queens)
  • 212. 单词搜索 II(212.word-search-ii)
      • 简单的回溯+剪枝(TLE)
      • 新方法:字典树Trie+回溯
  • 980. 不同路径III
    • [写法1]用vector存三维数组(内存超限)
    • [写法2]map存三元组(通过)
  • 五题小结

关于回溯

回溯跟枚举差不多。要注意“回溯”,别忘记“回”之前把之前的改动都复原。

37. 解数独(37.sudoku-solver)

在这里插入图片描述

leetcode37是解数独问题。本题保证有且仅有唯一解。

思路:先把空格子的位置存下来,然后对每一个空位置挨个枚举1-9。枚举之前,先建立一个一维数组,把要排除的数先排除,效率会高些。

class Solution {// 空格的信息int x[100], y[100], cnt = 0;bool dfs(int i, vector<vector<char>>& board) {if (i == cnt) return true;bool s[60] = {false};// 检查行、列for (int j = 0; j < 9; j++) s[board[x[i]][j]] = s[board[j][y[i]]] = true;// 检查九宫格for (int j = x[i] / 3 * 3; j < x[i] / 3 * 3 + 3; j++)for (int k = y[i] / 3 * 3; k < y[i] / 3 * 3 + 3; k++)s[board[j][k]] = true;// 枚举尝试1-9for (char c = '1'; c <= '9'; c++) {if (s[c] == false) {board[x[i]][y[i]] = c;if (dfs(i + 1, board))return true;}}board[x[i]][y[i]] = '.';return false;}public:void solveSudoku(vector<vector<char>>& board) {// 检索空格子for (int i = 0; i < 9; i++) {for (int j = 0; j < 9; j++) {if (board[i][j] == '.') {x[cnt] = i;y[cnt++] = j;}}}dfs(0, board);return;}
};

17. 电话号码的数字组合(17.letter-combinations-of-a-phone-number)

在这里插入图片描述
leetcode17是纯纯的枚举问题。

逐位处理那串数字,把记录好的当作参数string alreadyHave。由于这个形参是每递归一下就新开辟一个栈帧,所以这样写不涉及到“改动复原”的事。如果占用空间太大了,就需要把这个参数改为引用,那么就需要“复原”了。

class Solution {vector<string> ans;string d;void dfs(int index, string alreadyHave) // index是待处理下标{if (index == d.length()) {if (alreadyHave != "")ans.push_back(alreadyHave);return;}int num = d[index] - '0', start, end;if (num >= 2 && num <= 7) {start = (num - 2) * 3 + 'a';end = start + 2;}if (num == 7)end++;if (num == 8) {start = 't';end = 'v';}if (num == 9) {start = 'w';end = 'z';}for (int i = start; i <= end; i++) {dfs(index + 1, alreadyHave + (char)(i));}return;}public:vector<string> letterCombinations(string digits) {d = digits;dfs(0, "");return ans;}
};

51. N皇后(51.n-queens)

在这里插入图片描述

信息记录的不是棋盘格,而是皇后们的列索引

class Solution {int q[10];int n;vector<vector<string>> ans;void r(int k){if (k == n){// 0~n-1个皇后都摆好了,存答案vector<string> thisans;for (int i=0; i<n; i++){//q[i]个点,1个Q,n-1-q[i]个点string curr = "";int a = q[i];int b = n-1-q[i];while (a--)curr += '.';curr+='Q';while (b--)curr += '.';thisans.push_back(curr);}ans.push_back(thisans);return;}//这个皇后将会在k行、i列for (int i=0; i<n; i++){//依次和之前的每个皇后检查冲突int j;for (j=0; j<k; j++){if (i == q[j] || abs(j-k) == abs(i-q[j])) break;}if (j != k) continue;q[k] = i;r(k+1);}return;}
public:vector<vector<string>> solveNQueens(int n) {this->n = n;r(0);return ans;}
};

212. 单词搜索 II(212.word-search-ii)

简单的回溯+剪枝(TLE)

class Solution {vector<string> ans;int dx[4] = {0,0,1,-1};int dy[4] = {1,-1,0,0};int row, col;bool dfs(vector<vector<char>>& board, vector<string>& words, int wordIndex, int charIndex, int x, int y){if (charIndex == words[wordIndex].length()){ans.push_back(words[wordIndex]);words.erase(words.begin()+ wordIndex);return true;}if (x < 0 || x >= row || y < 0 || y >= col) return false;if (board[x][y] != words[wordIndex][charIndex]) return false;char tmp = board[x][y]; //必须备份board[x][y] = '#';for (int i=0; i<4; i++){int nx = x+dx[i], ny = y+dy[i]; //上下左右if (dfs(board, words, wordIndex, charIndex+1, nx, ny)){board[x][y] = tmp;return true;}}board[x][y] = tmp;return false;}
public:vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {row = board.size();col = board[0].size();for (int i=0; i<row; i++)for (int j=0; j<col; j++)for (int k=0; k<words.size(); )if (!dfs(board, words, k, 0, i, j))k++;return ans;}
};

通过了42/65个。
在这里插入图片描述

新方法:字典树Trie+回溯

struct TrieNode {string word = ""; //只有叶节点的word是非空的map<char, TrieNode *> child; //孩子节点的指针
};void insertTrie(TrieNode * root, string word) // 在字典树中插入新单词
{TrieNode * p = root;for (auto c : word) {if (p->child[c] == 0) //当前节点的孩子中没有word的某个字符(即变量c)p->child[c] = new TrieNode(); //新建一片叶子p = p->child[c]; //沿着树的节点从上往下捋}p->word = word; //因为单词表之内没有重复,所以最后一定停在新的叶子上
}class Solution {set<string> tmp_ans; //为了去重vector<string> ans;int dx[4] = {0, 0, 1, -1};int dy[4] = {1, -1, 0, 0};int row, col;bool dfs(vector<vector<char>>& board, int x, int y, TrieNode * root) {if (root->word != "") //走到了叶子{tmp_ans.insert(root->word); //找到,插入到返回值中//return true;}if (x < 0 || x >= row || y < 0 || y >= col)return false;if (root->child[board[x][y]] == 0)return false;// 上下左右继续找char tmp = board[x][y];board[x][y] = '#';for (int i = 0; i < 4; i++) {int nx = x + dx[i], ny = y + dy[i];if (dfs(board, nx, ny, root->child[tmp])) {board[x][y] = tmp;return true;}}board[x][y] = tmp;return false;}public:vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {TrieNode * root = new TrieNode(); //空字典树for (auto & word : words)insertTrie(root, word);row = board.size();col = board[0].size();for (int i = 0; i < row; i++)for (int j = 0; j < col; j++)dfs(board, i, j, root);for (auto & word : tmp_ans)ans.push_back(word);return ans;}
};

虽然还是超时,但可以把测试用例通过了,也就是超时的没那么离谱。可以继续优化。
在这里插入图片描述
最终的官方题解和我这个代码是高度相似的,所以针对卡常再修补修补即可通过。

980. 不同路径III

在二维网格 grid 上,有 4 种类型的方格:

1 表示起始方格。且只有一个起始方格。
2 表示结束方格,且只有一个结束方格。
0 表示我们可以走过的空方格。
-1 表示我们无法跨越的障碍。
返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。
每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。

状态压缩、记忆化dfs
用vector存三维数组,内存超限的代码。那么就尝试用map进行优化

[写法1]用vector存三维数组(内存超限)

class Solution {int row, col, dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};int dfs(int s, int x, int y, vector<vector<int>>& grid, vector<vector<vector<int>>>& f) {if (x < 0 || x >= row || y < 0 || y >= col) return 0;if (f[s][x][y] != -1) return f[s][x][y];int ans = 0;if (grid[x][y] == 2) {// 检查是否每一个无障碍方格都到过int i;for (i = 0; i < col * row; i++)if ((s >> i & 1) == 0 &&grid[i / col][i - i / col * col] == 0) {break;}if (i == col * row)ans = 1;elseans = 0;} else if ((s >> (col * x + y) & 1) == 1 || grid[x][y] == -1) {ans = 0;} else {grid[x][y] = -1;for (int i = 0; i < 4; i++) {int nx = x + dx[i], ny = y + dy[i];ans += dfs(s | 1 << (col * x + y), nx, ny, grid, f);}grid[x][y] = 0;}f[s][x][y] = ans;return ans;}public:int uniquePathsIII(vector<vector<int>>& grid) {row = grid.size();col = grid[0].size();vector<vector<vector<int>>> f(1 << (row * col), vector<vector<int>>(row, vector<int>(col, -1)));for (int i = 0; i < row; i++)for (int j = 0; j < col; j++)if (grid[i][j] == 1) {grid[i][j] = 0;               // 为了方便return dfs(0, i, j, grid, f); // i,j是起始点}return -1;}
};

在这里插入图片描述
(*  ̄︿ ̄)

[写法2]map存三元组(通过)

class Solution {// 为了节约内存,把f[1<<20][20][20]变为map,缺点是速度变慢map<pair<int, pair<int, int>>, int> f;int row, col, dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};int dfs(int s, int x, int y, vector<vector<int>>& grid) {if (x < 0 || x >= row || y < 0 || y >= col) return 0;if (f.count(make_pair(s, make_pair(x,y)))) return f[make_pair(s, make_pair(x,y))];int ans = 0;if (grid[x][y] == 2) {// 检查是否每一个无障碍方格都到过int i;for (i = 0; i < col * row; i++)if ((s >> i & 1) == 0 && grid[i / col][i - i / col * col] == 0)break;if (i == col * row) ans = 1;} else if ((s >> (col * x + y) & 1) == 1 || grid[x][y] == -1) {ans = 0;} else {grid[x][y] = -1;for (int i = 0; i < 4; i++) {int nx = x + dx[i], ny = y + dy[i];ans += dfs(s | 1 << (col * x + y), nx, ny, grid);}grid[x][y] = 0;}f[make_pair(s, make_pair(x,y))] = ans;return ans;}public:int uniquePathsIII(vector<vector<int>>& grid) {row = grid.size();col = grid[0].size();for (int i = 0; i < row; i++)for (int j = 0; j < col; j++)if (grid[i][j] == 1) {grid[i][j] = 0;               // 为了方便return dfs(0, i, j, grid); // i,j是起始点}return -1;}
};

继续优化:用map实现记忆化时,状态要压缩的彻底。比如本题要记忆的内容实际上是三维的(状态、x、y) 这三个不用写成三元组,一个32位的整型int就能存下来。

五题小结

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

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

相关文章

K次取反后最大化的数组和 加油站 分发糖果 柠檬水找零

1005.K次取反后最大化的数组和 力扣题目链接(opens new window) 给定一个整数数组 A&#xff0c;我们只能用以下方法修改该数组&#xff1a;我们选择某个索引 i 并将 A[i] 替换为 -A[i]&#xff0c;然后总共重复这个过程 K 次。&#xff08;我们可以多次选择同一个索引 i。&a…

安装算法依赖时版本报错,依赖之间对应版本

困惑了很久&#xff0c;毕竟不是计算机专业专业出身&#xff0c;才知道安装深度学习算法各个依赖之间是有版本对应关系的。 &#xff08;本文使我随笔记录&#xff0c;无价值&#xff09; 比如&#xff1a; 再比如&#xff1a; 由于我第一步安装cuda时就和其他博主不一致&…

Vue基础入门(2)- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程

Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程 文章目录 Vue基础入门&#xff08;2&#xff09;- Vue的生命周期、Vue的工程化开发和脚手架、Vue项目目录介绍和运行流程5 生命周期5.1 Vue生命周期钩子5.2 在creat…

docker pull 拉取失败,设置docker国内镜像

遇到的问题 最近在拉取nginx时&#xff0c;显示如下错误&#xff1a;Error response from daemon: Get “https://registry-1.docker.io/v2/”: net/http: request canceled (Client.Timeout exceeded while awaiting headers)。 这个的问题是拉取镜像超时&#xff0c;通过检索…

c语言经典测试题11

1.题1 #include <stdio.h> int main() { int a[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, *p a 5, *q NULL; *q *(p5); printf("%d %d\n", *p, *q); return 0; }上述代码的运行结果是什么呢&#xff1f; 我们来分析一下&#xff1a;我们创建了一个数…

第1题:两数之和

题目内容&#xff1a; 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。…

数据持久层框架:MyBatis-Plus

数据持久层框架&#xff1a;MyBatis-Plus 前言注解代码生成器CURD接口Service CRUD 接口Mapper CRUD 接口 条件构造器QueryWrapper和UpdateWrapperallEqeq、negt、ge、lt、lebetween、notBetweenlike、notLike、likeLeft、likeRight、notLikeLeft、notLikeRightisNull、isNotNu…

C 判断

判断结构要求程序员指定一个或多个要评估或测试的条件&#xff0c;以及条件为真时要执行的语句&#xff08;必需的&#xff09;和条件为假时要执行的语句&#xff08;可选的&#xff09;。 C 语言把任何非零和非空的值假定为 true&#xff0c;把零或 null 假定为 false。 下面…

UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found

UOS 20 安装redis 7.0.11 1、下载redis 7.0.112、安装redis 7.0.113、启动停止redis 7.0.114、安装过程问题记录 UOS 20 安装redis 7.0.11 安装redis 7.0.11时 make命令 报错 /bin/sh: cc: command not found、zmalloc.h:50:31: fatal error: jemalloc/jemalloc.h: No such fil…

代码随想录训练营第37天 | LeetCode 738.单调递增的数字、LeetCode 968.监控二叉树、

目录 LeetCode 738.单调递增的数字 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;贪心算法&#xff0c;思路不难想&#xff0c;但代码不好写&#xff01;LeetCode:738.单调自增的数字_哔哩哔哩_bilibili 思路 ​​​​​​LeetCode 968.监控二…

代码随想录算法训练营第十四天| 144. 二叉树的前序遍历 ,145. 二叉树的后序遍历,94. 二叉树的中序遍历

两种写法&#xff0c;递归和非递归写法 递归&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : va…

基于协同过滤的旅游推荐系统设计与实现

基于协同过滤的旅游推荐系统设计与实现 在当今旅游业蓬勃发展的背景下&#xff0c;人们对于旅游体验的需求日益增加&#xff0c;如何为用户提供更加个性化、精准的旅游推荐成为了旅游行业的一个重要课题。为解决这一问题&#xff0c;我们设计并实现了一个基于协同过滤的旅游推…

【设计模式 03】抽象工厂模式

一个具体的工厂&#xff0c;可以专门生产单一某一种东西&#xff0c;比如说只生产手机。但是一个品牌的手机有高端机、中端机之分&#xff0c;这些具体的属于某一档次的产品都需要单独建立一个工厂类&#xff0c;但是它们之间又彼此关联&#xff0c;因为都共同属于一个品牌。我…

android开发网络通信,带你彻底搞懂Android启动速度优化

实现方案 直接依赖 这种方式实现简单&#xff0c;但是耦合太严重&#xff0c;不方便维护与开发&#xff0c;当工程逐渐增大模块逐渐增多&#xff0c;依赖关系会非常复杂&#xff0c;不推荐这种方式。 事件或广播通信 EventBus&#xff1a; 我们非常熟悉的事件总线型的通信框…

Rust学习笔记:深度解析内存管理(二)

在这个信息爆炸的时代&#xff0c;学习一门新的编程语言不仅仅是为了找到一份好工作&#xff0c;更是为了打开思维的新窗口。Rust&#xff0c;作为一门注重安全、速度和并发的系统编程语言&#xff0c;正吸引着越来越多的年轻开发者的目光。今天&#xff0c;我们将一起深入探讨…

数据结构与算法:堆排序和TOP-K问题

朋友们大家好&#xff0c;本节内容来到堆的应用&#xff1a;堆排序和topk问题 堆排序 1.堆排序的实现1.1排序 2.TOP-K问题3.向上调整建堆与向下调整建堆3.1对比两种方法的时间复杂度 我们在c语言中已经见到过几种排序&#xff0c;冒泡排序&#xff0c;快速排序&#xff08;qsor…

微信小程序云开发教程——墨刀原型工具入门(安装以及基础使用教程)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

稀碎从零算法笔记Day4-LeetCode:交替合并字符串

前言&#xff1a;今天妹有深夜档&#xff0c;因为8点有个飞机 题型&#xff1a;字符串、双指针&#xff08;笔者没用这个思路&#xff09; 链接&#xff1a;1768. 交替合并字符串 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 著作权归作者所有。商业转…

JasperStudio中TextField文本框组件渲染之后,出现行间距不一致的问题

目录 1.1、问题描述 1.2、解决方案 1.1、问题描述 最近在处理线上遇到的一个问题,是有关JasperReports报表相关的问题,问题背景大概是这样的:我们的项目中使用了JasperReports来渲染报表,其中使用到了Text Field文本框组件,但是问题是渲染出来的数据直接会出现一些间距…

洛谷:P3068 [USACO13JAN] Party Invitations S(枚举、前缀和)

这题我们数据范围太大&#xff0c;用二维肯定是不行的&#xff0c;我们可以采用一维线性存储。 如题意&#xff0c;我们可以将每组奶牛编号都存在一维数组里面&#xff0c;只需记录每组的头尾指针就可以了。 如题中样例我们就可以存储成1 3 3 4 1 2 3 4 5 6 7 4 3 2 1 然后第…