回溯法

回溯法

剑指offer —66题 矩阵中的路径

 题目描述

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

代码如下

和原来讲过的一道题目一样,只不过输入稍微改了一下,这里可以用原来的模板。

```
class Solution {
private:   
    vector<vector<bool>>visited;
    int m,n;
    int d[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
    bool Area(int x,int y)
    {
         return x>=0&&x<m&&y>=0&&y<n;
    }
    bool help(char* board, char* word,int index,int x,int y)
    {
        if(index==strlen(word)-1)
            return board[x*n+y]==word[index];
        
        if(board[x*n+y]==word[index])
        {
            visited[x][y]=true;
            
            for(int i=0;i<4;i++)
            {
                int newx=x+d[i][0];
                int newy=y+d[i][1];
                
                if(Area(newx,newy)&& !visited[newx][newy]&&help(board,word,index+1,newx,newy))
                    return true;
            }           
            visited[x][y]=false;
        }
        
        return false; 
    }

public:
    bool hasPath(char* matrix, int rows, int cols, char* str){
        
        m=rows;
        if(m<=0)
            return false;
        n=cols;
        visited=vector<vector<bool>>(m,vector<bool>(n,false));
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(help(matrix,str,0,i,j))
                    return true;
        
        return false;

    }
};
```

剑指offer—67题   机器人运动范围

题目描述:

地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?

代码如下:

主要分析:这个题和二维平面上原来做的回溯题一样,要灵活的设计函数的返回值,函数的形式可以根据实际情况设计,
这里的help函数就是设计成了void形式,而res设置成了一个全局变量,这样就可以方便的进行统计,一定注意不要重复加。

```
class Solution {
private:
    vector<vector<bool>>visited;
    int m,n;
    int d[4][2]={{-1,0},{0,-1},{1,0},{0,1}};
    bool Area(int x,int y)
    {
        return x>=0&&x<m&&y>=0&&y<n;
    }
    
    bool Is_insert(int threshold,int x,int y)
    {
        int x_sum=0,y_sum=0;
        while(x>0)
        {
            x_sum+=x%10;
            x=x/10;
        }
        while(y>0)
        {
            y_sum+=y%10;
            y=y/10;
        }
        if(x_sum+y_sum<=threshold)
            return true;
        return false;
    }
    
    int res=0;
    void help(int threshold,int x,int y)
    {

        if(Is_insert(threshold,x,y)&&Area(x,y)&&!visited[x][y])
        {
            visited[x][y]=true;
            res++;
            //res=1+help(threshold,x-1,y)+help(threshold,x,y-1)+help(threshold,x+1,y)+help(threshold,x,y+1);

            for(int i=0;i<4;i++)
            {
                int newx=x+d[i][0];
                int newy=y+d[i][1];
                help(threshold,newx,newy);
            }
        }
        return ;
    }

public:
    int movingCount(int threshold, int rows, int cols)
    {
        if(rows<=0||threshold<0||cols<0)
            return 0;
        m=rows;
        n=cols;
        
        visited=vector<vector<bool>>(m,vector<bool>(n,false));
        help(threshold,0,0);
        return res;
    }
};
```

##### 也可以这样写,辅助函数;这样写的好处是,如果让统计每一块连通域的个数的时候,非常方便,res是一个局部变量,容易控制。可以仿照着LeetCode中的洪水的那个题,去提升,应该会不错。

```
    //int res=0;
    void help(int threshold,int x,int y,int &res)
    {

        if(Is_insert(threshold,x,y)&&Area(x,y)&&!visited[x][y])
        {
            visited[x][y]=true;
            res++;
            //res=1+help(threshold,x-1,y)+help(threshold,x,y-1)+help(threshold,x+1,y)+help(threshold,x,y+1);
            for(int i=0;i<4;i++)
            {
                int newx=x+d[i][0];
                int newy=y+d[i][1];
                help(threshold,newx,newy,res);
            }
        }
        return ;
    }

```

2017年软红笔试题(这部分来自网上内容整理)

题目: 给定一个mXn的矩阵,如下图所示,里面所有元素都非负,试找出一条从左上角到右下角的一条路径,要求路径上的数字之和最小。注:在某一个位置只有两种选择,向下或者向右。
“`
1    4    8    2    9
1    4    6    7    8
1    1    1    1    1
在以上矩阵中,其中数字之和最小的路径为1-1-1-1-1-1-1,其最小和为7.
请给出你的解题思路,并完成int min_sum_path(int *mat, int row, int col)。其中函数返回值为路径和的最小值,最后给出你算法的时间复杂度。
“`

思路一: 

可直接用回溯算法,每次从当前位置有2种选择,要么向右,要么向下,示例代码如下:`

``
#include <iostream>
#include<string>
#include<vector>
using namespace std;
 
//存放位置
int POSX[10];
int POSY[10];
 
void DFS(int depth, int i, int j, int *mat, int m, int n, int &cursum, int &minsum)
{
    //记录位置
    POSX[depth] = i;
    POSY[depth] = j;
    //迭代结束
    if (depth == n + m - 1)
    {
        return;
    }
    //更新最小值
    if (i == m - 1 && j == n - 1 && cursum < minsum)
        minsum = cursum;
    //右
    if (i<m && j + 1<n)
    {
        cursum += mat[i*n + (j + 1)];
        DFS(depth + 1, i, j + 1, mat, m, n, cursum, minsum);
        cursum -= mat[i*n + (j + 1)];
    }
    //下
    if (i + 1<m && j<n)
    {
        cursum += mat[(i + 1)*n + j];
        DFS(depth + 1, i + 1, j, mat, m, n, cursum, minsum);
        cursum -= mat[(i + 1)*n + j];
    }
 
}
 
int min_sum_path(int *mat, int row, int col)
{
    int cursum = mat[0];
    int minsum = 10000;
    //求最值
    DFS(0, 0, 0, mat, row, col, cursum, minsum);
 
    return minsum;
}
 
int main()
{
    int mat[] = { 1, 4, 8, 2, 9, 1, 4, 6, 7, 8, 1, 1, 1, 1, 1 };
    int row = 3;
    int col = 5;
    
    int answer = min_sum_path(mat,  row,  col);
    cout << "answer = " << answer << endl;
    
    //输出位置
    for (int i = 0; i<row + col - 1; i++)
    {
        cout << mat[POSX[i]* col + POSY[i]] << " ";
    }
    cout << endl;
 
    return 0;
}

```

思路二: 

动态规划,将该一维数组视为二维数组,由于每个位置只能往右或者往下走,不妨记F(i,j)表示从起点到位置(i,j)处的路径和的最小值,则状态转移方程为:
F(i+1,j+1) = min(F(i,j+1), F(i+1,j)) + mat[i+1][j+1]   其中 0< i + 1<row   0< j+1<col

初始条件为,对于第一行的位置,因为只能向右到达 ,所以 F(0,j) = sum(0,j)    j=0…col; 对于第一列的位置,只能从起点向下到达,所以 F(i,0) = sum(i,0)  i = 0…row

据此,可得代码如下:

```
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
 
int min_sum_path(int *mat, int row, int col)
{
    int i, j;
    int dp[100][100] = { 0 };
    dp[0][0] = mat[0];
    //第一行
    for ( j = 1; j < col; j++)
        dp[0][j] = dp[0][j - 1] + mat[j];
    //第一列
    for ( i = 1; i < row; i++)
        dp[i][0] = dp[i - 1][0] + mat[i*col];
    //其他位置
    for ( i = 1; i < row;i++)
        for (j = 1; j < col; j++)
            dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + mat[i*col + j];
    return dp[row-1][col-1];
}
int main()
{
    int mat[] = { 1, 4, 8, 2, 9, 1, 4, 6, 7, 8, 1, 1, 1, 1, 1 };
    int row = 3;
    int col = 5;
    
    int answer = min_sum_path(mat,  row,  col);
    cout << "answer = " << answer << endl;
    
    return 0;
}
```

 

    原文作者:分支限界法
    原文地址: https://blog.csdn.net/qq_20110551/article/details/81349636
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞