代码随想录算法训练营第29天 | 332.重新安排行程 51.N皇后 37.解数独

重新安排行程

Alt
这道题难点在于寻找合适的容器,来实现已知起点后的路径遍历递归与回溯,记录好起点与终点之间的映射关系。因为我们要优先挑选字母序小的路径,所以在上面说的起点向终点的映射中,终点最好按字母序排好,这样我们每次优先选择的都是字母序小的,一旦找到了一条合法路径,就能保证这条路径的字母序最小。
容器内的元素是有序的,我们可以想到map或者multiset这样的容器,为什么是multiset不是set呢?因为给出的机票是可能重复的,存在一个起点多次达到同一个终点的机票,元素可能重复,所以选择的是 multi 的。
但是multiset删除元素会导致迭代器失效,不可用。那又有一个问题:为什么需要删除元素呢?因为每一张机票只能使用一次,一个路径走过不能再走第二次,所以当走完一个起点-终点路径后,需要将这个路径删除避免重复。
这样我们选择map<string, int>来记录这一起点-终点路径还有多少剩余,在单层逻辑中只需要判断剩余路径是否大于0,就知道该不该遍历这一路径了。

class Solution{
public:
	vector<string> path;
	unordered_map<string, map<string, int>> targets;
	bool backtracking(int ticketNum){
		if(path.size() == ticketNum + 1){  // 如果当前的path中机场数目等于机票数目+1,说明已经用光了所有的机票
			return true;  // 需要有bool型返回型,找到第一条路径就是字母序最小的,立即返回
		}
		// target需要加上引用,不然不能更改原本targets变量中的余票数目
		for(pair<const string, int>& target : targets[path.back()]){
			if(target.second > 0){
				target.second--;
				path.push_back(target.first);  // 将当前字母序最小的机场加入路径
				if(backtracking(ticketNum))  return true;
				path.pop_back();
				target.second++;
			}
		}
		return false;
	}
	vector<string> findItinerary(vector<vector<string>>& tickets){
		path.clear();
		for(const vector<string>& vec : tickets){  // vec是map的key,所以要保持不变,用const
			targets[vec[0]][vec[1]]++;
		}
		path.push_back("JFK");  // 将起点加入path
		backtracking(tickets.size());
		return path;
	}
};

N皇后

Alt
N皇后是最典型的回溯问题。由于一行只能放一个皇后,一行一行地进行递归和回溯,同时在下棋时要注意判断是否合法。递归终止条件就是

class Solution{
public:
	vector<vector<string>> result;
	vector<string> board;
	bool isValid(int row, int col){
		for(int i = row - 1; i >= 0; i--){
			if(board[i][col] == 'Q')  return false;  // 判断这一列是不是已经有了皇后
		}
		// 再判断两个斜线是不是已经放了皇后
		for(int i = row - 1, j = col + 1; i >= 0 && j < board.size(); i--, j++){
			if(board[i][j] == 'Q')  return false;
		}
		for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--){
			if(board[i][j] == 'Q')  return false;
		}
		return true;
	}
	void backtracking(int n, int row){
		if(row == n){  // 所有的行都按要求填入了皇后,得到了一个可行解
			result.push_back(board);
			return;
		}
		for(int i = 0; i < n; i++){  // 遍历这一行的n列,分别填入皇后进行试探
			if(!isValid(row, i))  continue;  //判断当前位置放皇后是否有效
			board[row][i] = 'Q';
			backtracking(n, row + 1);
			board[row][i] = '.';
		}
	}
	vector<vector<string>> solveNQueens(int n){
		result.clear();
		string str(n, '.');
		board.resize(n, str);  // 构建一个全是.的棋盘
		backtracking(n, 0);  // 从第一行开始递归
		return result;
	}
};

解数独

Alt
Alt
这道题的解题思路和N皇后很像很像,但是这道题是要一个解,所以找到就要返回,不然一直回溯原本的数组就变回原样了。
同时这道题要一个个查看每一个位置

class Solution{
public:
	bool isValid(vector<vector<char>>& board, int row, int col, char val){
		for(int i = 0; i < board.size(); i++){
			if(board[i][col] == val)  return false;  // 这一列有没有重复的值
		}
		for(int i = 0; i < board.size(); i++){
			if(board[row][i] == val)  return false;
		}
		int start_row = row / 3 * 3;
		int start_col = col / 3 * 3;
		for(int i = start_row; i < start_row + 3; i++){
			for(int j = start_col; j < start_col + 3; j++){
				if(board[i][j] == val)  return false;  // 九宫格内有没有重复的值
			}
		}
		return true;
	}
	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 ch = '1'; ch <= '9'; ch++){
					if(!isValid(board, i, j, ch))  continue;  // 如果要填充的数字无效,跳过该数字
					board[i][j] = ch;
					if(backtracking(board))  return true;
					board[i][j] = '.';
				}
				return false;  // 1-9的数字都没法获得解,返回false,放弃这种填法
			}
		}
		return true;  // 全都遍历完也没返回,说明已经填好,返回true
	}
	void solveSudoku(vector<vector<char>>& board){
		backtracking(board);
	}
};
  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值