迷宫问题(MazePath)的求解——利用回溯法(backtracking)

迷宫问题(MazePath)的求解——利用回溯法(backtracking)

1. 迷宫问题的提法

  • 迷宫问题是典型的图的搜索问题。
  • 假设一个迷宫,只有一个入口和一个出口。如果从迷宫的入口到达出口,途中不出现行进方向错误,则得到一条最佳路线。
  • 为此,用一个二维数组maze[m][p]来表示迷宫。

    (1)当数组元素maze[i][j]=1 (0≤i≤m-1,1≤j≤p-1),表示该位置是墙壁,不能通行。

    (2)当数组元素maze[i][j]=0 (0≤i≤m-1,1≤j≤p-1),表示该位置是通路,可以通行。

2. 回溯法的概念

2.1 回溯法的定义

  • 回溯法(backtracking)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。

2.2 回溯法的思想

  • 回溯法将问题的候选解按某种顺序逐一枚举和检验。

    (1)当发现当前的候选解不可能是解时,就放弃它而选择下一个候选解。

    (2)如果当前的候选解除了不满足问题规模要求外,其他所有要求都已满足,则扩大当前候选解的规模继续试探。。

    (3)如果当前的候选解满足了包括问题规模在内的所有要求,则这个候选解将成为问题的一个解。

  • 注:

    (1)当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

    (2)扩大当前候选解的规模并继续试探的过程叫做向前试探。

    (3)用回溯法求解问题时常常使用递归方法进行试探,或使用栈帮助向前试探和回溯。

3. 用回溯法求解迷宫问题的算法原理

  • 在求解迷宫问题的过程中,当沿某一条路径一步步走向出口但发现进入死胡同走不通时,就回溯一步或多步,寻找其他可走的路径,这就是回溯。
  • 人在迷宫中任一时刻的位置可用数组行下标i和列下标j表示。从maze[i][j]出发,可能的前进方向有8个,按顺时针方向为N([i-1][j]),NE([i-1][j+1]),E([i][j+1]),SE([i+1][j+1]),S([i+1][j]),SW([i+1][j-1]),W([i][j-1]),NW([i-1][j-1])。
  • 设位置[i][j]标记为X,它实际是一系列交通路口。X周围有8个前进方向,分别代表8个前进位置。如果某一方向是0值,表示该方向有路可通,否则表示该方向已堵死。为了有效地选择下一位置,可以将从位置[i][j]出发可能的前进方向预先定义在一个表内。

    (1)可能的前进方向示意图:

    (2)前进方向表move:

    Move[q].dir move[q].a move[q].b
    “N” -1 0
    “NE” -1 1
    “E” 0 1
    “SE” 1 1
    “S” 1 0
    “SW” 1 -1
    “W” 0 -1
    “NW” -1 -1

4. 利用递归求解迷宫问题

4.1 迷宫的初始化及前进方向表的定义

  • 文件:MazeConfig.h

    
    #pragma once
    
    #include <iostream>
    
    #include <windows.h>
    
    using namespace std;
    
    //网格的结构定义
    struct GridType
    {
        int x;//网格的x坐标
        int y;//网格的y坐标
    };
    
    //位置在直角坐标下的偏移量的结构定义
    struct MoveTable
    {
        int a;//x方向上的偏移
        int b;//y方向上的偏移
        char *dir;//移动的方向
    };
    
    const int m = 14;//迷宫的行数
    const int p = 17;//迷宫的列数
    const int pathmark = 6;//迷宫通路网格标识值
    
    static int mark[m][p];//访问标记数组
    
    GridType entry = {1, 0};//迷宫入口网格坐标
    GridType exitus = {m-2, p-1};//迷宫出口网格坐标
    
    //各个方向的偏移表定义
    MoveTable moveTable[8] =
    {
        {-1, 0, "N"},
        {-1, 1, "NE"},
        {0, 1, "E"},
        {1, 1, "SE"},
        {1, 0, "S"},
        {1, -1, "SW"},
        {0, -1, "W"},
        {-1, -1, "NW"}
    };
    
    //初始化迷宫
    int Maze[m][p] =
    {
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
        { 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
        { 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1 },
        { 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1 },
        { 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
        { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1 },
        { 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
        { 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 },
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
    };
    
    //初始化访问标记数组
    void init_mark()
    {
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < p; j++)
            {
                mark[i][j] = 0;
            }
        }
    }
    
    //打印迷宫
    void print_maze()
    {
        cout << "======>MazePath" << endl;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < p; j++)
            {
                if (Maze[i][j] == pathmark)
                {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
                }
                else
                {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
                }
                cout << Maze[i][j] << " ";
            }
            cout << endl;
        }
    }

4.2 迷宫问题的递归求解算法实现

  • 文件:SeekPath.h

    
    #pragma once
    
    #include "MazeConfig.h"
    
    //从迷宫某一位置[i][j]开始,寻找通向出口的一条路径。
    //如果找到,则函数返回1。
    //如果没找到,则函数返回0。
    int SeekPath(GridType curGrid)
    {
        if ((curGrid.x == exitus.x) && (curGrid.y == exitus.y))
        {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
            cout << "======>SeekPath Success" << endl;
            return 1;//已到达出口,函数返回1。
        }
    
        GridType nextGrid;//下一个网格的位置
        for (int i = 0; i < 8; i++)//依次按每一个方向寻找通向出口的路径
        {
            nextGrid.x = curGrid.x + moveTable[i].a;
            nextGrid.y = curGrid.y + moveTable[i].b;
            if ((Maze[nextGrid.x][nextGrid.y] == 0) && (mark[nextGrid.x][nextGrid.y] == 0))
            {
                //下一位置可通,试探该方向
                mark[nextGrid.x][nextGrid.y] = 1;//标记为已访问过
                if (SeekPath(nextGrid) != 0)//从此位置递归试探
                {
                    //cout << "(" << nextGrid.x << "," << nextGrid.y << ")," << "Direction:" << moveTable[i].dir << ", ";
                    Maze[nextGrid.x][nextGrid.y] = pathmark;
                    return 1;//试探成功,逆向输出路径坐标
                }
            }
            //回溯,换一个方向再试探通向出口的路径。
        }
    
        if ((curGrid.x == entry.x) && (curGrid.y == entry.y))
        {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
            cout << "======>SeekPath Fail" << endl;
        }
    
        return 0;//无可通路到出口,函数返回0。
    }

4.3 主函数(main函数)的实现

  • 文件:main.cpp

    
    #include "SeekPath.h"
    
    int main()
    {
        print_maze();
        init_mark();
        if (SeekPath(entry) != 0)
        {
            //cout << "(" << entry.x << "," << entry.y << ")" << endl;
            Maze[entry.x][entry.y] = pathmark;
            print_maze();
        }
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
        system("pause");
        return 0;
    }

4.4 迷宫问题求解结果

  • 控制台输出,迷宫通路是绿色高亮显示的路径。

5. 利用栈求解迷宫问题

5.1 链表结点结构的定义

  • 文件:LinkNode.h

    
    #ifndef LINK_NODE_H_
    
    #define LINK_NODE_H_
    
    #include <iostream>
    
    #include <string>
    
    #include <strstream>
    
    using namespace std;
    
    template <class T>
    struct LinkNode         //链表结点类的定义
    {
        T data;             //数据域
        LinkNode<T> *link;  //指针域——后继指针
        //仅初始化指针成员的构造函数
        LinkNode(LinkNode<T>* ptr = NULL){ link = ptr; }
        //初始化数据与指针成员的构造函数
        LinkNode(const T& value, LinkNode<T>* ptr = NULL){ data = value; link = ptr; }
    };
    
    #endif /* LINK_NODE_H_ */
    

5.2 链式栈的类定义及其操作的实现

  • 文件:LinkedStack.h

    
    #ifndef LINKED_STACK_H_
    
    #define LINKED_STACK_H_
    
    #include "LinkNode.h"
    
    #include "Stack.h"
    
    template <class T>
    class LinkedStack
    {
    public:
        LinkedStack();                      //构造函数
        ~LinkedStack();                     //析构函数
    public:
        void Push(const T& x) ;         //新元素x进栈
        bool Pop(T& x);                 //栈顶元素出栈,并将该元素的值保存至x
        LinkNode<T>* getTop() const;    //获取栈顶结点
        bool IsEmpty() const;           //判断栈是否为空
        void MakeEmpty();               //清空栈的内容
    private:
        LinkNode<T> *top;   //栈顶指针,即链头指针
    };
    
    //构造函数
    template <class T>
    LinkedStack<T>::LinkedStack()
    : top(NULL)
    {
        cout << "$ 执行构造函数" << endl;
    }                       
    
    //析构函数
    template <class T>
    LinkedStack<T>::~LinkedStack()
    {
        cout << "$ 执行析构函数" << endl;
        MakeEmpty();
    }   
    
    //新元素x进栈
    template <class T>
    void LinkedStack<T>::Push(const T& x)
    {
        LinkNode<T> *newNode = new LinkNode<T>(x);
        newNode->link = top;
        top = newNode;
    }
    
    //栈顶元素出栈,并将该元素的值保存至x
    template <class T>
    bool LinkedStack<T>::Pop(T& x)
    {
        if (true == IsEmpty())
        {
            return false;
        }
        LinkNode<T> *curNode = top;
        top = top->link;
        x = curNode->data;
        delete curNode;
        return true;
    }
    
    //获取栈顶结点
    template <class T>
    LinkNode<T>* LinkedStack<T>::getTop() const
    {
        return top;
    }
    
    //判断栈是否为空
    template <class T>
    bool LinkedStack<T>::IsEmpty() const
    {
        return (NULL == top) ? true : false;
    }
    
    //清空栈的内容
    template <class T>
    void LinkedStack<T>::MakeEmpty()
    {
        LinkNode<T> *curNode = NULL;
        while (NULL != top)             //当链表不为空时,删去链表中所有结点
        {
            curNode = top;              //保存被删结点
            top = curNode->link;        //被删结点的下一个结点成为头结点
            delete curNode;             //从链表上摘下被删结点
        }
    }
    
    #endif /* LINKED_STACK_H_ */
    

5.3 迷宫的初始化及前进方向表的定义

  • 文件:MazeConfig.h

    
    #ifndef MAZECONFIG_H_
    
    #define MAZECONFIG_H_
    
    #include <iostream>
    
    #include <windows.h>
    
    using namespace std;
    
    //网格的结构定义
    struct GridType
    {
        int x;//网格的x坐标
        int y;//网格的y坐标
    };
    
    //位置在直角坐标下的偏移量的结构定义
    struct MoveTable
    {
        int a;//x方向上的偏移
        int b;//y方向上的偏移
        char *dir;//移动的方向
    };
    
    const int m = 14;//迷宫的行数
    const int p = 17;//迷宫的列数
    const int pathmark = 6;//迷宫通路网格标识值
    
    static int mark[m][p];//访问标记数组
    
    GridType entry = { 1, 0 };//迷宫入口网格坐标
    GridType exitus = { m - 2, p - 1 };//迷宫出口网格坐标
    
    //各个方向的偏移表定义
    MoveTable moveTable[8] =
    {
        { -1, 0, "N" },
        { -1, 1, "NE" },
        { 0, 1, "E" },
        { 1, 1, "SE" },
        { 1, 0, "S" },
        { 1, -1, "SW" },
        { 0, -1, "W" },
        { -1, -1, "NW" }
    };
    
    //初始化迷宫
    int Maze[m][p] =
    {
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
        { 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
        { 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1 },
        { 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1 },
        { 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
        { 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
        { 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1 },
        { 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 },
        { 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
        { 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 },
        { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
    };
    
    //初始化访问标记数组
    void init_mark()
    {
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < p; j++)
            {
                mark[i][j] = 0;
            }
        }
    }
    
    //打印迷宫
    void print_maze()
    {
        cout << "======>MazePath" << endl;
        for (int i = 0; i < m; i++)
        {
            for (int j = 0; j < p; j++)
            {
                if (Maze[i][j] == pathmark)
                {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
                }
                else
                {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
                }
                cout << Maze[i][j] << " ";
            }
            cout << endl;
        }
    }
    
    #endif /* MAZECONFIG_H_ */
    

5.4 迷宫问题的非递归求解算法实现

  • 文件:SeekPath.h

    
    #ifndef SEEKPATH_H_
    
    #define SEEKPATH_H_
    
    #include "MazeConfig.h"
    
    #include "LinkedStack.h"
    
    //从迷宫某一位置[i][j]开始,寻找通向出口[m][p]的一条路径。
    template <class T>
    void SeekPath(LinkedStack<T>* st, GridType curGrid)
    {
        GridType nextGrid;//下一个网格的位置
        st->Push(curGrid);
        while (st->IsEmpty() == false)
        {
            st->Pop(curGrid);
            for (int d = 0; d < 8; d++)
            {
                nextGrid.x = curGrid.x + moveTable[d].a;
                nextGrid.y = curGrid.y + moveTable[d].b;
                if ((nextGrid.x == exitus.x) && (nextGrid.y == exitus.y))
                {
                    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
                    cout << "======>SeekPath Success" << endl;
                    st->Push(curGrid);
                    st->Push(nextGrid);
                    return;
                }
                if ((Maze[nextGrid.x][nextGrid.y] == 0) && (mark[nextGrid.x][nextGrid.y] == 0))
                {
                    mark[nextGrid.x][nextGrid.y] = 1;//标记为已访问过
                    st->Push(curGrid);
                    curGrid = nextGrid;
                    d = 0;
                }
            }
        }
    
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
        cout << "======>SeekPath Fail" << endl;
    }
    
    template <class T>
    void MarkPath(LinkedStack<T>* st)
    {
        LinkNode<T> *curNode = st->getTop();
        while (NULL != curNode)
        {
            GridType item = curNode->data;
            Maze[item.x][item.y] = pathmark;
            curNode = curNode->link;
        }
    }
    
    #endif /* SEEKPATH_H_ */
    

5.5 主函数(main函数)的实现

  • 文件:main.cpp

    
    #include "SeekPath.h"
    
    int main(int argc, char* argv[])
    {
        print_maze();
        init_mark();
        LinkedStack<GridType> *linkedStack = new LinkedStack<GridType>;
        SeekPath(linkedStack, entry);
        MarkPath(linkedStack);
        print_maze();
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
        delete linkedStack;
        linkedStack = NULL;
        system("pause");
        return 0;
    }

5.6 迷宫问题求解结果

  • 控制台输出,迷宫通路是绿色高亮显示的路径。



参考文献:

[1]《数据结构(用面向对象方法与C++语言描述)(第2版)》殷人昆——第三章

[2]?百度搜索关键字:迷宫问题、回溯法、试探法

时间: 2024-11-07 16:14:33

迷宫问题(MazePath)的求解——利用回溯法(backtracking)的相关文章

利用回溯法求解背包问题

最近看完了利用回溯法求八皇后问题,最后成功求解到92种解法,然后在看利用贪心求解背包问题,突然想到其实也可以利用回溯法求解背包问题,本质上回溯法是一个穷举的方式在求. 回溯法求解出的结果肯定是正确的,这也可以验证自己所写的贪心算法的正确性. 问题描诉: 设定Wmax为最大重量,W[](0~n-1)为编号0~n-1的货物重量,V[](0~n-1)为其价值,x[]为其中解, 在wn=ΣXi*Wi<Wmax的条件下,求Vmax=ΣXi*Vi. 代码如下: //全局变量最大价值int maxvalue=

拉丁矩阵问题 利用回溯法的C++实现方案

这两天正好在赶算法设计的作业,这里把做的几个需要写代码的题放上来,方便以后查看. 1.题目要求 2.算法思想 这个题目基本思想是 利用回溯法,对于 m 行 n 列, 本质上就是一个二维数组, 我们可以将问题的解写成 x[1],x[2],x[3] - x[m*n], 那么对于每个点 x[i] 的取值实际上是 [1, n], 套用回溯法的算法框架,这里的 约束条件 ,就是同行,同列 没有相同的取值, 并且这里没有优化目标,只要类似 N后问题找出所有解就可以了. 另外,回溯时候的边界条件,需要特别注意

【LeetCode】回溯法 backtracking(共39题)

p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } [10]Regular Expression Matching [17]Letter Combinations of a Phone Number [22]Generate Parentheses (2019年2月13日) 给了一个N,生成N对括号的所有情况的字符串. n = 3 [ "((()))", "(()())", "(

回溯法(backtracking) 题目整理--------part2

N-Queens 模拟退火不会写 0.0 The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle. Each solution contains a distinct bo

算法复习笔记(回溯法,分支限界法)

回溯法 分支限界法 回溯法 回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法. 基本思想: 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树.当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯.(其实回溯法就是对隐式图的深度优先搜索

回溯法编程技巧

1. 什么是回溯法 引用一下维基百科对回溯法的介绍: 回溯法(英语:backtracking)是暴力搜索法中的一种. 对于某些计算问题而言,回溯法是一种可以找出所有(或一部分)解的一般性算法,尤其适用于约束满足问题(在解决约束满足问题时,我们逐步构造更多的候选解,并且在确定某一部分候选解不可能补全成正确解之后放弃继续搜索这个部分候选解本身及其可以拓展出的子候选解,转而测试其他的部分候选解). 在经典的教科书中,八皇后问题展示了回溯法的用例.(八皇后问题是在标准国际象棋棋盘中寻找八个皇后的所有分布

基于回溯法寻找哈密顿回路

回溯法是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树.当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯(其实回溯法就是对隐式图的深度优先搜索算法). 若用

回溯法理解

回溯法 基本思想: 构建问题的解空间树,在其解空间树中,从根节点出发,进行深度优先搜索.在搜索过程中,对解空间 树的每个结点进行判断,判断该结点是否包含问题的解,若肯定不包含,则跳过对以该结点为根的子树的 搜索,逐层向其祖先结点回溯.否则,则进入该子树,继续按深度优先策略搜索. 步骤: 1.针对所给问题,定义其解空间 2.确定易于搜索的解空间结构 3.深度优先搜索其解空间,并在搜索过程中用剪枝函数避免无效搜索 剪枝: 回溯法搜索空间树时,常用限界函数和约束函数避免无效搜索,其中约束函数将不满足约

[Leetcode] Backtracking回溯法解题思路

碎碎念: 最近终于开始刷middle的题了,对于我这个小渣渣确实有点难度,经常一两个小时写出一道题来.在开始写的几道题中,发现大神在discuss中用到回溯法(Backtracking)的概率明显增大.感觉如果要顺利的把题刷下去,必须先要把做的几道题题总结一下. 先放上参考的web: https://segmentfault.com/a/1190000006121957 http://summerisgreen.com/blog/2017-07-07-2017-07-07-算法技巧-backtr