这里记录二刷代码随想录的回溯部分,9.4号到9.8号,耗时5天。主要有以下几类问题:组合问题、分割问题、子集问题、排列问题和棋盘问题。其中基础的题型一般是不用去重的,在选择子节点上没有太多要求的,难题相反。
- 行程问题 问题十分抽象。如何选择行程,如何转换成回溯问题?这是我没怎么想明白的地方。和之前题目不同的是,这里的待选项比较复杂,是一个字符串“出发地,目的地”。我们该怎么选择?首先是要解决的一个问题是,如何构建映射关系?(其实这一步一开始也没想到,这一步的目的是什么?)
unordered_map<string, map<string, int>> targets;
unordered_map<出发地,<到达地,航班次数>> 为什么这么构造?把出发地当成键值的原因在于,在每一次选择下一个目的地的时候,我们是根据当前的出发地来选择的。之所以要加上航班次数,是要确保一张票飞一次。 映射关系的构建:
for (const vector<string>& vec : tickets) {
targets[vec[0]][vec[1]]++; // 记录映射关系
}
如何进行选择?和之前选择数不同,这里的选择比较复杂:
for (pair<const string, int>& target : targets[result[result.size() - 1]])
result[result.size() - 1] 表示的是结果的最后一个,也就是上一层的终点,作为起点。这里的选择的逻辑是,遍历键所对应的值,含义是对应当前这个地方所能去的目的地。没有这种思维,一开始遇到这个题还是有点儿难的,难在不晓得改用什么数据结构能解决这个问题。
unordered_map<string, map<string, int>> targets;
bool backtracking(int ticketNum, vector<string>& result) {
if (ticketNum + 1 == result.size()) {
return true;
}
for (pair<const string, int>& target : targets[result[result.size() - 1]]) {
if (target.second > 0) {
result.push_back(target.first);
target.second--;
if (backtracking(ticketNum, result)) return true;
result.pop_back();
target.second++;
}
}
return false;
}
- N皇后
N皇后问题可以将其规律总结为棋盘问题。但是相较于数独问题而言,其要判断的逻辑相对简单一些。 单层逻辑:
for (int col = 0; col < n; col++) {
if (isValid(row, col, chessboard, n)) { // 验证合法就可以放
chessboard[row][col] = 'Q'; // 放置皇后
backtracking(n, row + 1, chessboard);
chessboard[row][col] = '.'; // 回溯,撤销皇后
}
}
类似的,多了一个判断的语句,如果符合要求,就放皇后,不符合要求跳过。这里假如n=3时,返回的result是一个空的,因为row != n,无法取到n。因为只有在当前层(棋盘的一行)合理时,才会row+1传给回溯函数,然后再进行一个判断,判断row == n时,才会把chessboard放进result。
-
数独
bool backtracking(vector<vector<char>>& board) { for (int i = 0; i < board.size(); i++) { for (int j = 0; j < board.size(); j++) { if (board[i][j] != '.') continue; for (char k = '1'; k <= '9'; k++) { if (isValid(i, j, k, board)) { board[i][j] = k; if (backtracking(board)) return true; board[i][j] = '.'; } } return false; } } return true; }
数独问题难在需要判断行和列,因此需要双重遍历来进行判断,符合要求的对board进行赋值。和安排行程类似的是,回溯函数返回的bool类型的值,if(backtracking(board)) return true; // 行程问题中的语句是if (backtracking(ticketNum, result)) return true;