用回溯法解矩阵中的路径问题

题目

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

思路

matrix表示矩阵字符数组,str表示要搜索的字符串,先在matrix中找到str的第一个值,作为第0层(这里的第几层就是字符串str中的第几个值),然后进入递归函数,按照回溯法的基本结构,逐层访问。对于每一层,matrix的访问顺序是上->右->下->左,编号1、2、3、4,如果找到符合的值,就进入下一层。通过一个set类型的visited保存所有访问过的matrix节点下标,每次访问前检查,访问后insert。

回溯法的思想和代码框架

void backtrack (int t)  
{  
    if (t>n)   
        output(x); //已到叶子结点,输出结果  
    else  
        // f(n,t),g(n,t)表示当前扩展结点处未搜索过的子树的起始编号和终止编号
        for (int i=f(n,t);i<=g(n,t);i++) {  
            x[t]=h(i); // h(i):表示在当前扩展结点处x[t]的第i个可选值
            //constraint(t)为true表示在当前扩展结点处x[1:t]的取值满足问题的约束条件
            //bound(t)为true表示在当前扩展结点处x[1:t]的取值尚未导致目标函数越界
            if (constraint(t)&&bound(t))  
                backtrack(t+1);  
        }  
}

(原文链接:https://blog.csdn.net/u010089444/article/details/53908003)

原代码

#include <bits/stdc++.h>
using namespace std;
class Solution {
public:
   bool flag=false;
   set<int> visited;//需要存储所有访问过的节点,而不仅是上一个访问的
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
       int size=strlen(str);
       bool result=0;
       for(int i=0;i<strlen(matrix);i++){
          if(matrix[i]==str[0]){
             auto ret=visited.insert(i);//存储访问过的节点下标
             result=backtrack(matrix,str,1,size,rows,cols,i+1);
             if(result==0) visited.clear();//从这个点出发无解,需要清空set再访问下一个起点,不清空的话会对后面的新路径有影响
          } 
          if(result) return true;
       }
       return 0;
    }
   bool backtrack(char* matrix,char* str,int t,int size,int rows,int cols,int x){
//t表示当前的层数,size表示str的长度,x表示当前节点在matrix中的下标值+1.
      if(t>=size)
         return 1;
      for(int i=1;i<=4;i++){//回溯法的基本结构
         if(i==1){
            if(x-cols>=0&&matrix[x-cols-1]==str[t]&&visited.find(x-cols-1)==visited.end()){
               auto ret=visited.insert(x-cols-1);
               flag=flag|backtrack(matrix,str,t+1,size,rows,cols,x-cols);
               //这里为什么用或?见debug
            } 
         }
         else if(i==2) {
            if(x%cols!=0&&matrix[x+1-1]==str[t]&&visited.find(x)==visited.end()) {
               auto ret=visited.insert(x);
               flag=flag|backtrack(matrix,str,t+1,size,rows,cols,x+1);
            }
         }
         else if(i==3){
            if((x+cols)<=rows*cols&&matrix[x+cols-1]==str[t]&&visited.find(x+cols-1)==visited.end()){
               auto ret=visited.insert(x+cols-1);
               flag=flag|backtrack(matrix,str,t+1,size,rows,cols,x+cols);
            } 
         } 
         else{
            if(x%cols!=1&&matrix[x-1-1]==str[t]&&visited.find(x-2)==visited.end()){
               auto ret=visited.insert(x-2);
               flag=flag|backtrack(matrix,str,t+1,size,rows,cols,x-1);
            }  
         }
      }
      if(flag==true)
         return 1;
      else
         return 0;
   }
};

改进代码

class Solution {
public:
    bool hasPath(char* matrix, int rows, int cols, char* str)
    {
        bool flag[10000]={0};
       for(int i=0;i<rows;i++){
          for(int j=0;j<cols;j++){
             //把matrix想象成二维数组,循环遍历,找到第一个str元素对应的值的位置,再递归判断四周的值
             if(judge(matrix,i,j,rows,cols,flag,str,0))
               return true;
          }
       }
       return false;
    }
private:
   bool judge(char* matrix,int i,int j,int rows,int cols,bool* flag,char* str,int k){
      int index=i*cols+j;
      //递归终止条件
      //1.下标越界 2.值不相等 3.flag中相应的位为true,即点被访问过
      if(i<0||j<0||i>=rows||j>=cols||matrix[index]!=str[k]||flag[index]==true)
         return false;
      //k已经到达str末尾,并且末尾的满足上述条件,说明完全匹配
      if(k==strlen(str)-1)
         return true;
      flag[index]=true;
      //回溯,递归寻找,每次找到了就给k+1,找不到flag置为false且返回false
      if(judge(matrix,i-1,j,rows,cols,flag,str,k+1)||
      judge(matrix,i+1,j,rows,cols,flag,str,k+1)||
      judge(matrix,i,j-1,rows,cols,flag,str,k+1)||
      judge(matrix,i,j+1,rows,cols,flag,str,k+1))
         return true;
         //走到这说明此路不通,还原,再试其他路径
      flag[index]=false;
      return false;
   }
};

改进的点

  • matrix用二维数组下标来表示,更方便简洁
  • 抽象出三种失败情况作为递归终止条件,而不是在每种情况内部判断
  • c++中不能声明变长的数组,因此声明了一个可能足够的空间

Debug的点

  • 不能只保存上一个访问的点,因为所有走过的点都不能再访问,因此需要用一个全局的数据结构保存走过的路径
  • 有可能str的第一个值在matrix中出现过多次,因此就存在多个可能的路径,当否定了一个之后,需要清空visited才能避免影响后面的路径。
  • 递归的下一层返回时不能直接return,因为如果直接return就不给剩下的几种情况机会了,例子:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
旅行商问题是一个经典的组合优化问题,可以使用回溯算法来求。排列树是一种用于解决排列问题的搜索树结构,可以用来辅助回溯算法的实现。邻接矩阵是一种图的表示方法,可以用来存储图的边信息。 以下是使用回溯算法、排列树和邻接矩阵旅行商问题的C++代码示例: ```c++ #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAXN = 20; // 最大节点数量 int n; // 节点数量 int dis[MAXN][MAXN]; // 邻接矩阵 bool vis[MAXN]; // 标记节点是否已访问 int ans = INT_MAX; // 存储最小距离 void dfs(int cur, int sum, int cnt) { if (cnt == n) { // 所有节点都已遍历完 ans = min(ans, sum); return; } if (sum >= ans) { // 剪枝:当前路径长度已大于已知最优 return; } for (int i = 0; i < n; i++) { if (!vis[i]) { // 未访问过的节点 vis[i] = true; dfs(i, sum + dis[cur][i], cnt + 1); vis[i] = false; } } } int main() { cin >> n; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> dis[i][j]; } } for (int i = 0; i < n; i++) { vis[i] = true; dfs(i, 0, 1); // 从第i个节点出发,当前路径长度为0,已访问节点数量为1 vis[i] = false; } cout << ans << endl; return 0; } ``` 该代码通过输入邻接矩阵来表示图的信息,使用回溯算法从每个节点出发进行遍历,通过排列树枚举所有可能的路径,并使用邻接矩阵来计算路径长度。在遍历过程,使用剪枝来减少不必要的计算,提高算法效率。最后输出最小路径长度作为答案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值