LeetCode 332 重新安排行程
题目链接:332
方法1 直接当作排列问题
思路:看作是vector<vector<string>>
的排列问题,只不过排列时需要满足上一节点的终点等于当前节点的起点这一条件。因此,剪枝有2个条件:
- 当前机票的起点不等于上张机票的终点
- 当前机票存在树枝重复,即
used
数组记录
当存在多种有效行程时,选择字典排序小的,因此可以先对机票数组按每张机票的终点进行字典排序,从而保证在for
循环时,字典排序小的目的地优先处理。这样,搜索到的第一条有效行程即是字典排序最小的有效行程,通过给回溯函数设置返回值提前返回。
代码:
class Solution {
public:
vector<string> results;
vector<bool> used;
bool backtracking(vector<vector<string>>& tickets) {
if (results.size() == tickets.size()+1) {
return true;
}
for (int i=0; i<tickets.size(); ++i) {
if (used[i]) continue;
if (tickets[i][0] != results.back()) continue;
results.push_back(tickets[i][1]);
used[i] = true;
if (backtracking(tickets)) return true;
used[i] = false;
results.pop_back();
}
return false;
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
results.clear();
used = vector<bool>(tickets.size(), false);
sort(tickets.begin(), tickets.end(),
[](const vector<string>& s1, const vector<string>& s2) {
return s1[1] < s2[1];
});
results.push_back("JFK");
backtracking(tickets);
return results;
}
};
方法2 使用map
思路:不同于在每层递归中都遍历tickets
寻找下一可行的机票,先通过一次遍历将起点终点信息记录在unordered_map<string, map<string, int>>
中,key
记录起点信息,value
即map<string, int>
记录每一起点对应的可行终点及可行次数,次数为该起点终点的机票张数。并且map
本身有序,默认递增,即字典值小的在前。这样,每层递归时,可通过起点快速查询可行的终点即次数,避免了tickets
的重复遍历。
代码:
class Solution {
public:
unordered_map<string, map<string, int>> ticket_map;
bool backtracking(vector<string>& results, int path_len) {
if (results.size() == path_len) return true;
for (auto& next : ticket_map[results.back()]) {
if (next.second == 0) continue;
results.push_back(next.first);
next.second--;
if (backtracking(results, path_len)) return true;
next.second++;
results.pop_back();
}
return false;
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
ticket_map.clear();
for (int i=0; i<tickets.size(); ++i) {
ticket_map[tickets[i][0]][tickets[i][1]]++;
}
vector<string> result = {"JFK"};
backtracking(result, tickets.size()+1);
return result;
}
};
LeetCode 51 N 皇后
题目链接:51
方法1 记录使用过的列和斜线
思路:每行选取一个位置为Q
,这样不会有多个皇后处于同一行,此时递归层数等于n
。选取col
时,通过记录的使用过的列和斜线used
来判断该位置是否可行。具体而言,当放置一个皇后(col, row)
时,列可直接记录;两条对角线分别为斜率为
1
1
1 和
−
1
-1
−1 的直线,因此可算出该位置对角线的截距为row-col
和col-row
,通过记录使用过的截距来记录不可行的对角线。
代码:
class Solution {
public:
vector<string> path;
vector<vector<string>> result;
vector<bool> used_x;
vector<bool> used_pos_diag;
vector<bool> used_neg_diag;
void backtracking(int y, int n) {
if (path.size() == n) {
result.push_back(path);
return;
}
for (int x=0; x<n; ++x) {
if (used_x[x]) continue;
int pos_diag_offset = y-x;
int neg_diag_offset = y+x;
if (used_pos_diag[pos_diag_offset+(n-1)]) continue;
if (used_neg_diag[neg_diag_offset]) continue;
string row(n, '.');
row[x] = 'Q';
path.push_back(row);
used_x[x] = true;
used_pos_diag[pos_diag_offset+(n-1)] = true;
used_neg_diag[neg_diag_offset] = true;
backtracking(y+1, n);
used_neg_diag[neg_diag_offset] = false;
used_pos_diag[pos_diag_offset+(n-1)] = false;
used_x[x] = false;
path.pop_back();
}
}
vector<vector<string>> solveNQueens(int n) {
used_x = vector<bool>(n, false);
used_pos_diag = vector<bool>(2*n-1, false);
used_neg_diag = vector<bool>(2*n-1, false);
path.clear();
result.clear();
backtracking(0, n);
return result;
}
};
方法2 遍历判断列和斜对角
思路:在判断位置(col,row)
时,遍历[0,row-1]
行中对应列和斜对角的元素是否有Q
。
代码:
class Solution {
public:
vector<vector<string>> result;
bool isValid(int n, int row, int col, vector<string>& chessboard) {
for (int i=0; i<row; i++) {
if (chessboard[i][col] == 'Q') return false;
}
for (int r=row-1, c=col-1; r>=0 && c>=0; r--, c--) {
if (chessboard[r][c] == 'Q') return false;
}
for (int r=row-1, c=col+1; r>=0 && c<n; r--, c++) {
if (chessboard[r][c] == 'Q') return false;
}
return true;
}
void backtracking(int n, int row, vector<string>& chessboard) {
if (row == n) {
result.push_back(chessboard);
return;
}
for (int i=0; i<n; i++) {
if (!isValid(n, row, i, chessboard)) continue;
chessboard[row][i] = 'Q';
backtracking(n, row+1, chessboard);
chessboard[row][i] = '.';
}
}
vector<vector<string>> solveNQueens(int n) {
vector<string> chessboard(n, string(n, '.'));
backtracking(n, 0, chessboard);
return result;
}
};
LeetCode 37 解数独
题目链接:37
方法1 使用一个一维index递归二维表
思路:递归函数中采用一个一维的index
来指示当前网格,处理完成后在下层递归中处理下个网格。若当前网格非空,则无需填数,直接进入下层递归。由于数独找到一个解即可返回,因此递归函数需要返回值bool
。
代码:
class Solution {
public:
bool isValid(int row, int col, char num, vector<vector<char>>& board) {
for (int r=0; r<board.size(); r++) {
if (board[r][col] == num) return false;
}
for (int c=0; c<board.size(); c++) {
if (board[row][c] == num) return false;
}
int hgrid_row = row / 3;
int hgrid_col = col / 3;
for (int r=hgrid_row*3; r<hgrid_row*3+3; r++) {
for (int c=hgrid_col*3; c<hgrid_col*3+3; c++) {
if (board[r][c] == num) return false;
}
}
return true;
}
bool backtracking(int index, vector<vector<char>>& board) {
if (index == board.size() * board.size()) {
return true;
}
int row = index / 9;
int col = index % 9;
if (board[row][col] != '.') {
if (backtracking(index+1, board)) return true;
return false;
}
for (int i=1; i<=9; i++) {
if (!isValid(row, col, '0'+i, board)) continue;
board[row][col] = '0'+i;
if (backtracking(index+1, board)) return true;
board[row][col] = '.';
}
return false;
}
void solveSudoku(vector<vector<char>>& board) {
backtracking(0, board);
}
};
方法2 二维递归
思路:在每层递归中,都从头开始,找到第一个空格,填数,再进入下层递归找下一个数。
代码:
class Solution {
public:
bool isValid(int row, int col, char num, vector<vector<char>>& board) {
for (int r=0; r<board.size(); r++) {
if (board[r][col] == num) return false;
}
for (int c=0; c<board.size(); c++) {
if (board[row][c] == num) return false;
}
int hgrid_row = row / 3;
int hgrid_col = col / 3;
for (int r=hgrid_row*3; r<hgrid_row*3+3; r++) {
for (int c=hgrid_col*3; c<hgrid_col*3+3; c++) {
if (board[r][c] == num) return false;
}
}
return true;
}
bool backtracking(vector<vector<char>>& board) {
for (int r=0; r<board.size(); ++r) {
for (int c=0; c<board[r].size(); ++c) {
if (board[r][c] != '.') continue;
for (int i=1; i<=9; ++i) {
if (!isValid(r, c, '0'+i, board)) continue;
board[r][c] = '0'+i;
if (backtracking(board)) return true;
board[r][c] = '.';
}
return false;
}
}
return true;
}
void solveSudoku(vector<vector<char>>& board) {
backtracking(board);
}
};