算法学习——LeetCode力扣回溯篇4
332. 重新安排行程
332. 重新安排行程 - 力扣(LeetCode)
描述
给你一份航线列表 tickets ,其中 tickets[i] = [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。
所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。
例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。
假定所有机票至少存在一种合理的行程。且所有的机票 必须都用一次 且 只能用一次。
示例
示例 1:
输入:tickets = [[“MUC”,“LHR”],[“JFK”,“MUC”],[“SFO”,“SJC”],[“LHR”,“SFO”]]
输出:[“JFK”,“MUC”,“LHR”,“SFO”,“SJC”]
示例 2:
输入:tickets = [[“JFK”,“SFO”],[“JFK”,“ATL”],[“SFO”,“ATL”],[“ATL”,“JFK”],[“ATL”,“SFO”]]
输出:[“JFK”,“ATL”,“JFK”,“SFO”,“ATL”,“SFO”]
解释:另一种有效的行程是 [“JFK”,“SFO”,“ATL”,“JFK”,“ATL”,“SFO”] ,但是它字典排序更大更靠后。
提示
- 1 <= tickets.length <= 300
- tickets[i].length == 2
- fromi.length == 3
- toi.length == 3
- fromi 和 toi 由大写英文字母组成
- fromi != toi
代码解析
回溯遍历(超时)
回溯遍历每一种可能
当出现第一种可能的路线,之间加入。
当出现新的可能路线,与老路线对比,如果字典排序小于,则替换老路线
class Solution {
public:
vector<string> resul;
vector<string> path;
void backtraking(vector<vector<string>>& tickets , string Indnx , vector<bool> &used)
{
//找到路线,看是否替换老的路径
//如果没有老路径直接加入,如果相同就返回,如果不同路径按照字典比较
if(path.size()==tickets.size()+1)
{
if(resul.empty()==1)
{
resul = path;
return;
}
else if(resul == path) return;
else
{
for(int j=0 ;j<path.size();j++)
{
for(int k=0 ;k<3;k++)
{
if(resul[j][k] == path[j][k]) continue;
else if(resul[j][k] < path[j][k])return;
else if(resul[j][k] > path[j][k])
{
resul.clear();
resul = path;
return;
}
}
}
}
// cout<<"resu: ";
// for(auto i:resul) cout<<i<<' ';
// cout<<endl;
return;
}
for(int i=0 ; i<tickets.size();i++)
{
//如果当前机票使用过,或者当前机票目的地不对,跳过
if(used[i]==true || tickets[i][0] != Indnx) continue;
//如果当前机票可用,则加入路径
if(used[i]== false && tickets[i][0] == Indnx)
{
used[i] = true;
path.push_back(tickets[i][1]);
//递归,确定递归找的新机票。下一站机票的开始机场,就是当前机票的目的地机场
backtraking(tickets,tickets[i][1],used);
used[i] = false;
path.pop_back();
}
}
return;
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
vector<bool> used(tickets.size(),false);
path.push_back("JFK");
backtraking(tickets,"JFK",used);
return resul;
}
};
排序再回溯
先对输入票排序,其中排序按照票的目的地的字典减少排序(因为出发点是确定的,目的地多种找最优解)
之后回溯遍历找路线,发现的第一个路线即为最优路线
class Solution {
public:
//按飞机票目的地(字符串vector第二个参数)字典减小排序
class compare
{
public:
bool operator()( const vector<string> tickets1 ,const vector<string> tickets2 )
{
if((tickets1[1])[0] < (tickets2[1])[0]) return 1;
else if((tickets1[1])[0] == (tickets2[1])[0])
{
if((tickets1[1])[1] < (tickets2[1])[1]) return 1;
else if((tickets1[1])[1] == (tickets2[1])[1])
{
if((tickets1[1])[2] < (tickets2[1])[2]) return 1;
else return 0;
}
return 0;
}
return 0;
}
};
vector<string> resul;
vector<string> path;
bool find = false;
void backtraking(vector<vector<string>>& tickets , string Indnx , vector<bool> &used)
{
//找到一个路径就不找了,直接是最优路径
if(find == true ) return;
if(path.size()==tickets.size()+1)
{
resul = path;
find =true;
return;
}
for(int i=0 ; i<tickets.size();i++)
{
if(used[i]==true || tickets[i][0] != Indnx) continue;
if(used[i]== false && tickets[i][0] == Indnx)
{
used[i] = true;
path.push_back(tickets[i][1]);
backtraking(tickets,tickets[i][1],used);
used[i] = false;
path.pop_back();
}
}
return;
}
vector<string> findItinerary(vector<vector<string>>& tickets) {
vector<bool> used(tickets.size(),false);
sort(tickets.begin(),tickets.end(),compare());
// for(auto i:tickets)
// {
// cout<<'[';
// for(auto j:i)
// {
// cout<<j<<' ';
// }
// cout<<']';
// }
path.push_back("JFK");
backtraking(tickets,"JFK",used);
return resul;
}
};
51. N 皇后
51. N 皇后 - 力扣(LeetCode)
描述
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例
示例 1:
输入:n = 4
输出:[[“.Q…”,“…Q”,“Q…”,“…Q.”],[“…Q.”,“Q…”,“…Q”,“.Q…”]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
提示
1 <= n <= 9
代码解析
递归遍历
主要是去除同行和同列
class Solution {
public:
vector<vector<string>> result;
vector<int>path;
//求绝对值
int abs(int a)
{
if (a < 0) return -a;
else return a;
}
void backtraking(int n ,vector<bool> &used ,int deep)
{
//当深度大于n时返回
if(deep > n) return;
//当路径为最大深度,找到路径存入
if(path.size() == n)
{
//将数字路径转换成字符串路径
vector<string> path_s;
for(int i=0;i<n;i++)
{
string tmp ;
for(int k=0;k<n;k++) tmp +='.';//建立n个.
for(int j=0;j<n;j++) if(j==path[i]) tmp[j] = 'Q';//在应该放Q的位置放Q
path_s.push_back(tmp);//转换好的字符串存入路径
}
//存入结果
result.push_back(path_s);
return;
}
//层次循环,找到每一行的点。
for(int i=0 ; i<n; i++)
{
if(used[i] == true) continue; //该点用过了,跳过。一个树枝的点只能用一次
pair<int,int> x(deep,i);//当前可能有效点
bool flag = true;
//当前点,和path路径里面所有点依次对比
for(int j =0 ; j < path.size() ;j++)
{
pair<int,int> y(j,path[j]);//path之前加入的点
//检测当前点与之前路径点,是否在一列一行和对角线
if( abs(x.first - y.first)==0 || abs(x.second - y.second)==0
|| abs(x.first - y.first) == abs(x.second - y.second) )
flag = false;
}
//检测是合格点,加入path
if(flag == true)
{
//记录该点使用
used[i] = true;
path.push_back(i);
//递归,深度+1(找下一行)
backtraking(n,used,deep+1);
path.pop_back();
used[i] = false;
}
}
return;
}
vector<vector<string>> solveNQueens(int n) {
vector<bool> used(n,false);
backtraking(n ,used,0);
return result;
}
};
37. 解数独
37. 解数独 - 力扣(LeetCode)
描述
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例
示例 1:
输入:board = [[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]
输出:[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示
- board.length == 9
- board[i].length == 9
- board[i][j] 是一位数字或者 ‘.’
- 题目数据 保证 输入数独仅有一个解
代码解析
class Solution {
public:
bool cheack( vector<vector<char>>& board , int row ,int col ,int val)
{
for(int i=0 ; i< board[0].size() ;i++) //检查行
{
if(board[row][i] == val )
{
return false;
}
}
for(int i=0 ; i< board.size() ;i++) //检查列
{
if(board[i][col] == val)
{
return false;
}
}
int startRow = (row / 3)*3;
int startCol = (col / 3)*3;
for(int i=startRow ; i < startRow + 3 ;i++) //检查小方块
{
for(int j=startCol ; j< startCol + 3 ;j++)
{
if(board[i][j] == val)
{
return false;
}
}
}
return true;
}
bool backtarking(vector<vector<char>>& board)
{
for(int i=0 ; i< board.size() ; i++) //递归行
{
for(int j=0 ; j<board[0].size() ;j++)//递归列
{
if(board[i][j] != '.') continue;//已有的跳过
for( char k = '1' ; k<='9';k++)
{
if(cheack(board,i,j,k)) //检查当前k是否符合
{
board[i][j] = k;
if(backtarking(board)) return true; //当找到一组成功的,就不接着找了直接返回true
board[i][j] = '.';
}
}
return false ;
}
}
return true;//行和列都满足了,返回找到
}
void solveSudoku(vector<vector<char>>& board) {
bool tmp = backtarking(board);
}
};