请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false
来源:力扣(LeetCode)
分析:经典回溯,但此题只要求返回true or false,能返回所有路径吗?
friend ostream &operator<<(ostream &out, const vector<vector<char>> &board)
{
//重载 << ,用于调试输出二维数组
for (auto i : board)
{
for (auto j : i)
{
out << j << " ";
}
out << endl;
}
out << endl;
return out;
}
bool backtrack(vector<vector<char>> &board, int row, int col,
const string &word, int idx)
{
//正确返回终止条件,找到一个正确分支即终止
if (idx == word.size())
return true;
//数组越界终止条件
if (row < 0 || row >= board.size() ||
col < 0 || col >= board[0].size())
return false;
//如果表格当前字符不等于欲匹配字符,剪掉此分支
//这样还在增长的分支就是可能正确的分支
if (word[idx] != board[row][col])
return false;
//设置标记位,如果后续回到此位置,比对字符即知
board[row][col] = '*';
//往四个方向走,有一个方向返回true,程序返回true
if (backtrack(board, row - 1, col, word, idx + 1) ||
backtrack(board, row + 1, col, word, idx + 1) ||
backtrack(board, row, col - 1, word, idx + 1) ||
backtrack(board, row, col + 1, word, idx + 1))
return true;
//回溯。走不通时该位置还原为本来的字母
board[row][col] = word[idx];
//cout<
//没有分支返回true。程序返回false
return false;
}
bool exist(vector<vector<char>> &board, string word)
{
if (board.empty() || board[0].empty())
return word.empty();
for (int row = 0; row < board.size(); ++row)
{
for (int col = 0; col < board[0].size(); ++col)
{
//暴力搜索每一点作为起点
if (backtrack(board, row, col, word, 0))
return true;
}
}
return false;
};
地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 1:
输入:m = 3, n = 1, k = 0
输出:1
根据上面一题直接写出此题的代码,提交结果为超时,本地运行超时用例时等待很久,说明有死循环。本着“不放弃每一段代码”的精神,我决定debug后再看书上的答案
class Solution {
public:
int K;
friend ostream &operator<<(ostream &out, const vector<vector<char>> &board)
{
for (auto i : board)
{
for (auto j : i)
{
out << j << " ";
}
out << endl;
}
out << endl;
return out;
}
bool cant_move(int row, int col, int k)
{
int cnt = 0;
while (row or col)
{
cnt += row % 10 + col % 10;
row /= 10;
col /= 10;
}
return cnt > k;
}
struct arrayHash{
//避免哈希冲突
int operator()(const array<int, 2> &p) const {
return p[0] * 1e2 + p[1]; }
};
unordered_set<array<int, 2>,arrayHash> um;
void backtrack(vector<vector<char>> &board, int row, int col)
{
//以下3个条件进行剪枝
//1、数组越界终止条件
if (row < 0 || row >= board.size() ||
col < 0 || col >= board[0].size())
return;
//2、标志位,防止重复遍历
if ('o' != board[row][col])
return;
//3、检测该点是否符合题目条件
if (cant_move(row, col, K))
return;
//设置标记位,如果后续回到此位置,比对字符即知
board[row][col] = '*';
//cout << board;
array<int, 2> p;
p[0] = row, p[1] = col;
if(um.find(p)==um.end()){
um.insert(p); //放入哈希表
}
//往四个方向走,
backtrack(board, row - 1, col);
backtrack(board, row + 1, col);
backtrack(board, row, col - 1);
backtrack(board, row, col + 1);
//回溯。走不通时该位置还原为本来的字母
board[row][col] = 'o';
//cout << board;
//没有分支返回true。程序返回false
return;
}
int movingCount(int m, int n, int k) {
vector<vector<char>> b(m,vector<char>(n,'o'));
K=k;
backtrack(b, 0, 0);
return um.size();
}
};
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
分析:纯剪枝,无回溯
class Solution {
vector<string> ans;
int N;
public:
vector<string> generateParenthesis(int n) {
N = n;
DFS("",0,0);
return ans;
}
void DFS(string s,int left,int right){
//两个剪枝条件
if(left < right) return;
if(left > N) return;
//终止递归条件
if(s.length() == 2*N){
ans.push_back(s);
return;
}
//分支
DFS(s+"(",left+1,right);
DFS(s+")",left,right+1);
}
};
copy了份代码,加点注释分析下
class Solution {
public:
vector<vector<string>> ans;
vector<vector<string>> solveNQueens(int n) {
//这里一定要写n,给record初始化
vector<int> record(n);
//字符串s初始化为n个点
string s="";
for(int i=0;i<n;i++){
s+='.';
}
//棋盘初始化为n个向量,每个都是n个点,即n*n棋盘
vector<string> temp(n, s);
helper(0, n, temp, record);
return ans;
}
/*
l表示行数,n为题目中的n,temp为棋盘,recode记录?
*/
void helper(int l, int n, vector<string>&temp, vector<int>& record){
//正常终止递归条件,即棋盘填满了
if(l==n){
ans.push_back(temp);
return;
}
//从左到右依次填入棋子
for(int i=0;i<n;i++){
//从左到右依次填入皇后
record[l]=i;
//如果不能填入,什么也不做,相当于剪枝
//如果能填入皇后,则填入
if(isok(record, l)){
temp[l][i]='Q';
//填入本行的皇后后,递归寻找下一行皇后的位置
helper(l+1, n, temp, record);
//回溯,即取消在该点填入皇后
temp[l][i]='.';
}
}
}
//检查皇后能否填入的算法,row是递归程序当前的行
bool isok(vector<int>& record, int row){
for(int i=0;i<row;i++){
if(record[i]==record[row]||row-record[row]==i-record[i]||row+record[row]==i+record[i])return false;
}
return true;
}
};
以n = 4不剪枝为例,每次递归程序处理一行。例如(0,0)填入后,又有(1,0)、(1,1)、(1,2)、(1,3)四种填法,第三行又有4种填法。关键是填入皇后(1,0)后,递归到下一层,递归返回后又重新填入(1,1),实现了每一层的4个分支
以下是我的解法:
#include
using namespace std;
int cnt = 0;
void outPut(const vector<vector<char>> &b)
{
for (int i = 0; i < b.size(); i++)
{
for (int j = 0; j < b.size(); j++)
{
cout << b[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
void mark(int x, int y, vector<vector<char>> &b)
{
int n = b.size();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
b[x][j] = '+';
b[i][y] = '+';
if (i + j == x + y or i - j == x - y)
{
b[i][j] = '+';
}
}
}
b[x][y] = 'x';
//outPut(b);
}
void dfs(int level, vector<vector<char>> &b)
{
if (level == b.size())
{
outPut(b);
cnt++;
return;
}
for (int i = 0; i < b.size(); i++)
{
if (b[level][i] == '.')
{
vector<vector<char>> tmp = b;
mark(level, i, b);
dfs(level + 1, b);
b = tmp;
//outPut(b);
}
}
}
int main()
{
int N;
cin >> N;
vector<vector<char>> b(N, vector<char>(N, '.')); //棋盘
dfs(0, b);
//cout << cnt << endl;
system("pause");
}
class Solution
{
set<char> st1;
set<char> st2;
public:
bool cheak(char c, set<char> &st)
{
if (c <= 57 && c >= 49)
{
if (st.find(c) != st.end())
return 0;
else
st.insert(c);
}
return 1;
}
bool isValidSudoku(vector<vector<char>> &board)
{
for (int i = 0; i < 9; i++)
{
st1.clear();
st2.clear();
for (int j = 0; j < 9; j++)
{
if (!cheak(board[i][j], st1))
return 0;
if (!cheak(board[j][i], st2))
return 0;
}
}
for (int u = 0; u < 3; u++)
{
for (int v = 0; v < 3; v++)
{
st1.clear();
for (int i = 3 * u; i < 3 * u + 3; i++)
{
for (int j = 3 * v; j < 3 * v + 3; j++)
{
if (!cheak(board[i][j], st1))
return 0;
}
}
}
}
return 1;
}
};
class Solution {
public:
bool solveSudoku(vector<vector<char>>& board) {
for ( int i = 0 ;i < board.size();i++){
for(int j=0;j<board[i].size();j++){
if(board[i][j]=='.'){
//开始填入
for(char c='1';c<='9';c++){
if(isValid(board,i,j,c)){
board[i][j]=c;//填入
if(solveSudoku(board)) return true;//解完
else board[i][j]='.';//下层返回错,回溯
}
}
return false;//填完了1-9 都不行 就返回错
}
}
}
return true;
}
private:
bool isValid(vector<vector<char>>& board,int row,int col,char c){
for(int i=0;i<9;i++){
if(board[i][col]==c) return false;
if(board[row][i]==c) return false;
if(board[3*(row/3)+i/3][3*(col/3)+i%3]==c) return false;
}
return true;
}
};