第四章 搜索

深度优先搜索

计算数的全排列
#include<iostream>
#include<vector>
#include<iterator>
#include<algorithm>

using namespace std;

/*
将1-n全排列,目前到了从左到右的step位
box:从左到右存储
flags:1-n哪些已经用了
*/
void dfs(vector<int> & box, vector<bool> & flags, int step, int n)
{
    if (step == n)  //如果当前到n,说明0~n-1已经存了1~n,停止搜索
    {
        ostream_iterator<int> o_iter(cout, "");
        copy(box.begin(), box.end(), o_iter);
        cout << endl;
        return;
    }

    //此时站在第step位(从左到右),将没使用的数按1~3尝试
    for (int i = 1; i <= n; ++i)
    {
        if (flags[i - 1] == false)  //如果这个数是未使用的
        {
            //开始尝试使用
            box[step] = i;
            flags[i - 1] = true;
            //在这种使用方式下,处理下一位
            dfs(box, flags, step + 1, n);
            //把数放回,尝试下一个数
            flags[i - 1] = false;
        }
    }
}

int main(void)
{
    int n = 3;  //输入1-3的全排列
    vector<int> box(n);
    vector<bool> flags(n, false);
    dfs(box, flags, 0, n);  //从左边第一位开始尝试
    return 0;
}
深度优先搜索把每一种可能都去尝试一遍(for),当前步解决的情况下进入下一步,而下一步解决的方法和当前步是完全一样的。
//深度优先搜索的基本模型
void dfs(int step)
{
    判断边界
    尝试每一种可能 for(i = 1; i <= n; ++i)
    {
        继续下一步 dfs(step + 1);
    }
    返回
}
再次尝试用深度优先搜索解决暴力枚举
将数字1~9填入等式“??? + ??? = ???”使等式成立
#include<iostream>
#include<vector>

using namespace std;

void dfs(vector<int> & box, vector<bool> & flags, int step, int n, int & total)
{
    if (step == n)
    {
        int a = box[0] * 100 + box[1] * 10 + box[2];
        int b = box[3] * 100 + box[4] * 10 + box[5];
        int c = box[6] * 100 + box[7] * 10 + box[8];
        if (a + b == c)  //判断等式是否满足
        {
            cout << box[0] << box[1] << box[2] << '+'
                << box[3] << box[4] << box[5] << '='
                << box[6] << box[7] << box[8] << endl;
            ++total;
        }
        return;
    }

    for (int i = 1; i <= n; ++i)
    {
        if (flags[i - 1] == false)
        {
            //开始尝试使用
            box[step] = i;
            flags[i - 1] = true;
            //在这种使用方式下,处理下一位
            dfs(box, flags, step + 1, n, total);
            //把数放回,尝试下一个数
            flags[i - 1] = false;
        }
    }
}

int main(void)
{
    int n = 9;  //输入1-9的全排列,但是要使得???+???=???等式成立
    vector<int> box(n);
    vector<bool> flags(n, false);
    int total = 0;  //设置变量用于存储所有情况
    dfs(box, flags, 0, n, total);  //从左边第一位开始尝试

    cout << total / 2 << endl;

    return 0;
}

深度优先搜索迷宫

迷宫由n行m列的单元格组成,每个单元格要么是空地,要么是障碍物,任务是找到一条从迷宫的起点通往目的位置的最短路径。
按照右下左上的顺序尝试路径搜索。
#include<iostream>
#include<vector>
#include<algorithm>
#include<climits>
#include<iterator>

using namespace std;

struct Point
{
    int x;
    int y;
};

void dfs(vector<vector<int>> & map, vector<vector<bool>> & flags,  //地图及地图标记
    Point curr, Point end,  //当前位置及目的位置
    int step, vector<Point> & path, int & min, vector<Point> & min_path)  //当前步长和当前路径,以及最短长度和最短路径
{
    if (curr.x == end.x && curr.y == end.y)
    {
        if (step < min)
        {
            min = step;
            min_path.resize(path.size());
            copy(path.begin(), path.end(), min_path.begin());
        }
        return;
    }

    static const vector<vector<int>> next = {
        {0, 1},  //向右
        {1, 0},  //向下
        {0, -1},  //向左
        {-1, 0}  //向上
    };

    for (const auto & direction : next)
    {
        //计算下一个点的坐标
        Point trypoint;
        trypoint.x = curr.x + direction[0];
        trypoint.y = curr.y + direction[1];

        //判断是否越界
        if (trypoint.x < 0 || trypoint.x >= map.size() 
            || trypoint.y < 0 || trypoint.y >= map[0].size())
            continue;

        if (map[trypoint.x][trypoint.y] == 0 && flags[trypoint.x][trypoint.y] == false)
        {
            //该点不是障碍且在路径中
            flags[trypoint.x][trypoint.y] = true;  //标记这个点已经走过
            path.push_back(trypoint);
            dfs(map, flags, trypoint, end, step + 1, path, min, min_path);  //下一个点
            path.pop_back();
            flags[trypoint.x][trypoint.y] = false;  //尝试结束,取消这个点的标记
        }
    }
}

int main(void)
{
    int rows = 5;  //行数
    int cols = 4;  //列数
    vector<vector<int>> map = {
        {0, 0, 1, 0},
        {0, 0, 0, 0},
        {0, 0, 1, 0},
        {0, 1, 0, 0},
        {0, 0, 0, 1},
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));  //行走标记
    //(向下为x,向右为y)
    //起点
    Point start;
    start.x = 0;
    start.y = 0;
    //终点
    Point end;
    end.x = 3;
    end.y = 2;

    vector<Point> path;  //搜索过程中尝试保存的路径
    path.push_back(start);  //路径的起点一定是(0, 0)
    vector<Point> min_path;  //最短路径
    int min = INT_MAX;  //最短路径长度
    dfs(map, flags, start, end, 0, path, min, min_path);  //从起点开始搜索

    cout << min << endl;
    for (auto iter = min_path.begin(); iter != min_path.end(); ++iter)
    {
        cout << '(' << iter->x << ',' << iter->y << ')';
        if (iter == min_path.end() - 1)
            break;
        cout << " ==> ";
    }
    cout << endl;

    //地图和路线的打印
    ostream_iterator<int> o_iter(cout, " ");
    cout << "原图" << endl;
    for (const auto & line : map)
    {
        copy(line.begin(), line.end(), o_iter);
        cout << endl;
    }
    cout << "走起" << endl;
    for (const auto & point : min_path)
        map[point.x][point.y] = 2;
    for (const auto & line : map)
    {
        copy(line.begin(), line.end(), o_iter);
        cout << endl;
    }

    return 0;
}
发明深度优先算法的是John E.Hopcroft和Robert E.Tarjan。1971~1972年,他们在斯坦福大学研究图的连通性(任意两点是否可以相互到达)和平面性(图中所有的边相互不交叉。在电路板上设计布线的时候,要求线与线不能交叉。这就是平面性的一个实际应用。)时,发明了这个算法,因此获得了1986年的图灵奖。
在授奖仪式上,当年全国象棋程序比赛的优胜者说他的程序用的就是深度优先算法,这是以奇制胜的关键。此外Tarjan是另外两位图灵奖得主Robert W.Floyd和Donald E.Knuth的学生。

广度优先搜索迷宫

深度优先搜索是沿着某条路直到走不通的时候回起点继续尝试,而广度优先搜索是通过一层层扩展的方法来搜索的。
使用队列模拟。
#include<iostream>
#include<vector>
#include<iterator>

using namespace std;

struct Point
{
    int x;
    int y;
    int last;  //上一个点,从哪个点来的,对第一个点无意义
};

int main(void)
{
    int rows = 5;  //行数
    int cols = 4;  //列数
    vector<vector<int>> map = {
        {0, 0, 1, 0},
        {0, 0, 0, 0},
        {0, 0, 1, 0},
        {0, 1, 0, 0},
        {0, 0, 0, 1},
    };
    static const vector<vector<int>> next = {
        { 0, 1 },  //向右
        { 1, 0 },  //向下
        { 0, -1 },  //向左
        { -1, 0 }  //向上
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));  //行走标记
    //(向下为x,向右为y)
    //起点
    Point start;
    start.x = 0;
    start.y = 0;
    //终点
    Point end;
    end.x = 3;
    end.y = 2;

    vector<Point> search;  //不用queue是因为要还原路径
    start.last = 0;
    search.push_back(start);
    int header = 0;
    bool isfind = false;

    while (header != search.size() && !isfind)  //队列不为空
    {
        for (const auto & direction : next)
        {
            Point trypoint;
            trypoint.x = search[header].x + direction[0];
            trypoint.y = search[header].y + direction[1];
            if (trypoint.x < 0 || trypoint.x >= map.size()
                || trypoint.y < 0 || trypoint.y >= map[0].size())  //判断是否越界
                continue;
            if (map[trypoint.x][trypoint.y] == 0 && flags[trypoint.x][trypoint.y] == false)
            {
                //该点不是障碍且在路径中
                //宽度优先搜索每个点只入队一次,不需要将flags还原
                flags[trypoint.x][trypoint.y] = true;
                trypoint.last = header;
                search.push_back(trypoint);
            }
            if (trypoint.x == end.x && trypoint.y == end.y)
            {
                //广度优先搜索找到一条路径就停止
                isfind = true;
                break;
            }
        }
        ++header;
    }

    //严格来说必须判断isfind才能确定是不是找到之后才跳出来的

    vector<Point> path;
    int curr_index = search.size() - 1;  //如果是找到之后跳出来,那么最后一个点一定是终点
    while (true)
    {
        path.push_back(search[curr_index]);
        if (curr_index == search[curr_index].last)  //已到起点,跳出
            break;
        curr_index = search[curr_index].last;
    }

    for (auto iter = path.rbegin(); iter != path.rend(); ++iter)
    {
        cout << '(' << iter->x << ',' << iter->y << ')';
        if (iter == path.rend() - 1)
            break;
        cout << " ==> ";
    }
    cout << endl;

    //地图和路线的打印
    ostream_iterator<int> o_iter(cout, " ");
    cout << "原图" << endl;
    for (const auto & line : map)
    {
        copy(line.begin(), line.end(), o_iter);
        cout << endl;
    }
    cout << "走起" << endl;
    for (const auto & point: path)
        map[point.x][point.y] = 2;
    for (const auto & line : map)
    {
        copy(line.begin(), line.end(), o_iter);
        cout << endl;
    }

    return 0;
}
1959年,Edward F.Moore率先在“如何从迷宫中寻找出路”这一问题中提出了广度优先搜索算法。1961年,C.Y.Lee在“电路板布线”这一问题中也独立提出了相同的算法。

再解炸弹人

枚举法没考虑是否能走到对应坐标点放置炸弹
可以通过广度优先搜索或者深度优先搜索来枚举所有可以到达的点,然后在这些可以到达的点上分别统计可以消灭的敌人数。
广度优先搜索解决方案
#include<iostream>
#include<vector>
#include<string>
#include<queue>

using namespace std;

struct Point
{
    int x;
    int y;
};

int getnum(const vector<string> & map, int i, int j)
{
    //在某个点放置炸弹能够消灭的敌人数
    int sum = 0;
    int x = i;
    int y = j;
    while (map[x][y] != '#')  //向下
    {
        if (map[x][y] == 'G')  //是敌人
            ++sum;
        ++x;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向上
    {
        if (map[x][y] == 'G')
            ++sum;
        --x;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向右
    {
        if (map[x][y] == 'G')
            ++sum;
        ++y;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向左
    {
        if (map[x][y] == 'G')
            ++sum;
        --y;
    }
    return sum;
}

int main(void)
{
    vector<string> map = {
        "#############",
        "#GG.GGG#GGG.#",
        "###.#G#G#G#G#",
        "#.......#..G#",
        "#G#.###.#G#G#",
        "#GG.GGG.#.GG#",
        "#G#.#G#.#.#.#",  //#G#.#G#.#.###,为了测试修改
        "##G...G.....#",
        "#G#.#G###.#G#",
        "#...G#GGG.GG#",
        "#G#.#G#G#.#G#",
        "#GG.GGG#G.GG#",
        "#############"
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));
    static const vector<vector<int>> next = {
        { 0, 1 },  //向右
        { 1, 0 },  //向下
        { 0, -1 },  //向左
        { -1, 0 }  //向上
    };

    queue<Point> search;
    search.push({ 3, 3 });  //队首,起始位置

    //统计最多的敌人数和坐标,假设起点最大,再四方广搜
    int max_count = getnum(map, 3, 3);
    int max_x = 3;
    int max_y = 3;

    //两重循环枚举每一点
    while (!search.empty())
    {
        for (const auto & direction : next)
        {
            Point trypoint;
            trypoint.x = search.front().x + direction[0];
            trypoint.y = search.front().y + direction[1];

            if (trypoint.x < 0 || trypoint.x >= map.size()
                || trypoint.y < 0 || trypoint.y >= map[0].size())  //判断是否越界
                continue;

            if (map[trypoint.x][trypoint.y] == '.' && flags[trypoint.x][trypoint.y] == false)
            {
                //该点不是障碍并且没走过
                flags[trypoint.x][trypoint.y] = true;  //没走过就走起
                search.push(trypoint);
                int sum = getnum(map, trypoint.x, trypoint.y);
                if (sum > max_count)
                {
                    max_count = sum;
                    max_x = trypoint.x;
                    max_y = trypoint.y;
                }
            }
        }
        search.pop();  //当前点扩展完,出队
    }

    cout << '(' << max_x << ',' << max_y << ")==>" << max_count << endl;

    return 0;
}
深度优先搜索解决方案
每走到一个新点就统计该点可以消灭的敌人数,并从该点继续尝试往下走,直到无路可走的时候返回,再尝试走其他方向,直到将所有可以走到的点都访问一遍,程序结束。
#include<iostream>
#include<vector>
#include<string>

using namespace std;

int getnum(const vector<string> & map, int i, int j)
{
    //在某个点放置炸弹能够消灭的敌人数
    int sum = 0;
    int x = i;
    int y = j;
    while (map[x][y] != '#')  //向下
    {
        if (map[x][y] == 'G')  //是敌人
            ++sum;
        ++x;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向上
    {
        if (map[x][y] == 'G')
            ++sum;
        --x;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向右
    {
        if (map[x][y] == 'G')
            ++sum;
        ++y;
    }
    x = i;
    y = j;
    while (map[x][y] != '#')  //向左
    {
        if (map[x][y] == 'G')
            ++sum;
        --y;
    }
    return sum;
}

void dfs(const vector<string> & map, vector<vector<bool>> & flags, int x, int y,
    int & max_x, int & max_y, int & max_count)  //记录最优值
{
    //统计当前点
    int sum = getnum(map, x, y);
    if (sum > max_count)
    {
        max_count = sum;
        max_x = x;
        max_y = y;
    }

    static const vector<vector<int>> next = {
        { 0, 1 },  //向右
        { 1, 0 },  //向下
        { 0, -1 },  //向左
        { -1, 0 }  //向上
    };
    //从该点开始深度搜索每个方向
    for (const auto & direction : next)
    {
        int try_x = x + direction[0];
        int try_y = y + direction[1];
        if (try_x < 0 || try_x >= map.size() || try_y < 0 || try_y >= map[0].size())
            continue;  //判断是否越界
        if (map[try_x][try_y] == '.' && flags[try_x][try_y] == false)
        {
            //该点不是障碍并且还没走过
            flags[try_x][try_y] = true;
            dfs(map, flags, try_x, try_y, max_x, max_y, max_count);  //尝试下一点
            //因为没有还原,所以终点就是能走到的点全部都标记了
        }
    }
}

int main(void)
{
    vector<string> map = {
        "#############",
        "#GG.GGG#GGG.#",
        "###.#G#G#G#G#",
        "#.......#..G#",
        "#G#.###.#G#G#",
        "#GG.GGG.#.GG#",
        "#G#.#G#.#.#.#",  //#G#.#G#.#.###,为了测试修改
        "##G...G.....#",
        "#G#.#G###.#G#",
        "#...G#GGG.GG#",
        "#G#.#G#G#.#G#",
        "#GG.GGG#G.GG#",
        "#############"
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));

    //统计最多的敌人数和坐标,假设起点最大,再四方广搜
    int max_count = getnum(map, 3, 3);
    int max_x = 3;
    int max_y = 3;
    dfs(map, flags, 3, 3, max_x, max_y, max_count);

    cout << '(' << max_x << ',' << max_y << ")==>" << max_count << endl;

    return 0;
}

宝岛冒险(着色问题)

用二维矩阵表示地图,数字表示海拔,0表示海洋,1-9表示陆地,给定一个降落位置,算出降落地所在岛的面积(格子数)。
广度优先搜索解决方案
从队列中取出点进行扩展(四个方向),当扩展出的点大于0时就加入队列,直到队列扩展完毕。所有被加入队列的点的总数就是小岛面积。
#include<iostream>
#include<vector>
#include<queue>
#include<iomanip>

using namespace std;

struct Point
{
    int x;
    int y;
};

void bfs(vector<vector<int>> & map, vector<vector<bool>> & flags, int x, int y, int color)
{
    static const vector<vector<int>> next = {
        { 0, 1 },  //向右
        { 1, 0 },  //向下
        { 0, -1 },  //向左
        { -1, 0 }  //向上
    };

    queue<Point> search;
    search.push({ x, y });  //起始坐标
    flags[x][y] = true;

    while (!search.empty())
    {
        for (const auto & direction : next)
        {
            Point trypoint;
            trypoint.x = search.front().x + direction[0];
            trypoint.y = search.front().y + direction[1];
            if (trypoint.x < 0 || trypoint.x >= map.size()
                || trypoint.y < 0 || trypoint.y >= map[0].size())
                continue;  //判断是否越界
            if (map[trypoint.x][trypoint.y] >= 1 &&
                map[trypoint.x][trypoint.y] <= 9 &&
                flags[trypoint.x][trypoint.y] == false)
            {
                //每个点只入队一次,所以需要标记
                flags[trypoint.x][trypoint.y] = true;
                search.push(trypoint);
            }
        }
        //当前点处理完毕,染色,出队
        map[search.front().x][search.front().y] = color;
        search.pop();
    }
}

int main(void)
{
    vector<vector<int>> map = {
        {1, 2, 1, 0, 0, 0, 0, 0, 2, 3},
        {3, 0, 2, 0, 1, 2, 1, 0, 1, 2},
        {4, 0, 1, 0, 1, 2, 3, 2, 0, 1},
        {3, 2, 0, 0, 0, 1, 2, 4, 0, 0},
        {0, 0, 0, 0, 0, 0, 1, 5, 3, 0},
        {0, 1, 2, 1, 0, 1, 5, 4, 3, 0},
        {0, 1, 2, 3, 1, 3, 6, 2, 1, 0},
        {0, 0, 3, 4, 8, 9, 7, 5, 0, 0},
        {0, 0, 0, 3, 7, 8, 6, 0, 1, 2},
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 0}
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));
    //输出原始地图
    for (const auto & line : map)
    {
        for (const auto & point : line)
            cout << setw(3) << point;
        cout << endl;
    }

    //给出一个降落点就得出一个岛屿
    //遍历整个地图,把所有可降落点搜索一次并标记,找出所有岛屿
    int color = -1;
    for (int i = 0; i < map.size(); ++i)
    {
        for (int j = 0; j < map[i].size(); ++j)
        {
            if (map[i][j] >= 1 && map[i][j] <= 9)  //被填充的肯定是-1、-2、-3……
            {
                bfs(map, flags, i, j, color);
                --color;
            }
        }
    }

    //输出染色地图
    cout << "共有" << (-color - 1) << "个岛屿" << endl;
    for (const auto & line : map)
    {
        for (const auto & point : line)
            cout << setw(3) << point;
        cout << endl;
    }

    return 0;
}
深度优先搜索解决方案
这种方法又叫着色法——以某个点为源点对其邻近的点进行着色。
#include<iostream>
#include<vector>
#include<iomanip>

using namespace std;

void dfs(vector<vector<int>> & map, vector<vector<bool>> & flags, int x, int y, int color)
{
    static const vector<vector<int>> next = {
        { 0, 1 },  //向右
        { 1, 0 },  //向下
        { 0, -1 },  //向左
        { -1, 0 }  //向上
    };

    map[x][y] = color;  //对当前点进行着色

    for (const auto & direction : next)
    {
        int tx = x + direction[0];
        int ty = y + direction[1];

        if (tx < 0 || tx >= map.size() || ty < 0 || ty >= map[0].size())
            continue;  //判断是否越界

        if (map[tx][ty] >= 1 && map[tx][ty] <= 9 && flags[tx][ty] == false)
        {
            //是陆地并且还没搜索过
            flags[tx][ty] = true;
            dfs(map, flags, tx, ty, color);
        }
    }
}

int main(void)
{
    vector<vector<int>> map = {
        {1, 2, 1, 0, 0, 0, 0, 0, 2, 3},
        {3, 0, 2, 0, 1, 2, 1, 0, 1, 2},
        {4, 0, 1, 0, 1, 2, 3, 2, 0, 1},
        {3, 2, 0, 0, 0, 1, 2, 4, 0, 0},
        {0, 0, 0, 0, 0, 0, 1, 5, 3, 0},
        {0, 1, 2, 1, 0, 1, 5, 4, 3, 0},
        {0, 1, 2, 3, 1, 3, 6, 2, 1, 0},
        {0, 0, 3, 4, 8, 9, 7, 5, 0, 0},
        {0, 0, 0, 3, 7, 8, 6, 0, 1, 2},
        {0, 0, 0, 0, 0, 0, 0, 0, 1, 0}
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));
    //输出原始地图
    for (const auto & line : map)
    {
        for (const auto & point : line)
            cout << setw(3) << point;
        cout << endl;
    }

    //给出一个降落点就得出一个岛屿
    //遍历整个地图,把所有可降落点搜索一次并标记,找出所有岛屿
    int color = -1;
    for (int i = 0; i < map.size(); ++i)
    {
        for (int j = 0; j < map[i].size(); ++j)
        {
            if (map[i][j] >= 1 && map[i][j] <= 9)  //被填充的肯定是-1、-2、-3……
            {
                flags[i][j] = true;
                dfs(map, flags, i, j, color);
                --color;
            }
        }
    }

    //输出染色地图
    cout << "共有" << (-color - 1) << "个岛屿" << endl;
    for (const auto & line : map)
    {
        for (const auto & point : line)
            cout << setw(3) << point;
        cout << endl;
    }

    return 0;
}
这就是求一个图中独立子图的个数,这个算法叫做Floodfill漫水填充法(种子填充法)。Floodfill在计算机图形学中有着非常广泛的运用,比如图像分割、物体识别等等。另外Windows下画图软件的油漆桶就是基于这个算法的,当需要给某个密闭区域涂色或者更改某个密闭区域的颜色时,程序自动选中与种子点(鼠标单击点)周边颜色相同的区域,接着将该区域替换成指定的颜色。PhotoShop的魔术棒选择工具也可以基于这个算法实现。
具体算法描述:查找种子点周边的点,将与种子点颜色相近的点(设置阈值以确定)入队作为新种子,并对新入队的种子也进行同样的扩展操作,这样就选取了和最初种子相近颜色的区域。

水管工游戏

只有两种管道,一种弯管一种直管。弯管有4种状态,直管有2种状态。
  • 上右
  • 右下
  • 下左
  • 左上
  • 左右
  • 上下
出水口有四个方向
从最左边进,从最右边出
#include<iostream>
#include<vector>

using namespace std;

enum PIPE_TYPE {
    TREE, L_UR, L_RD, L_DL, L_LU, I_H, I_V
};

enum IN_DIRECTION {
    IN_LEFT, IN_UP, IN_RIGHT, IN_DOWN
};

struct Point
{
    int x;
    int y;
};

void dfs(const vector<vector<PIPE_TYPE>> & map, vector<vector<bool>> & flags,
    int x, int y, IN_DIRECTION direction, vector<Point> & path)
{
    if (x == map.size() - 1 && y == map[0].size())
    {
        for (const auto & point : path)
            cout << '(' << point.x << ',' << point.y << ')' << ' ';
        cout << endl;
        return;
    }

    //判断是否越界
    if (x < 0 || x >= map.size() || y < 0 || y >= map[0].size())
        return;

    //判断这个管道是否已经使用过
    if (flags[x][y] == true)
        return;

    flags[x][y] = true;  //标记使用当前管道

    path.push_back({ x, y });

    //如果当前是直管
    if (map[x][y] == I_H || map[x][y] == I_V)
    {
        //进水口在左边,只能使用I_H情况
        if (direction == IN_LEFT)
            dfs(map, flags, x, y + 1, IN_LEFT, path);
        //进水口在上边,只能使用I_V情况
        else if (direction == IN_UP)
            dfs(map, flags, x + 1, y, IN_UP, path);
        //进水口在右边,只能使用I_H情况
        else if (direction == IN_RIGHT)
            dfs(map, flags, x, y - 1, IN_RIGHT, path);
        //进水口在下面,只能使用I_V情况
        else
            dfs(map, flags, x - 1, y, IN_DOWN, path);
    }
    //如果当前是弯管
    else
    {
        if (direction == IN_LEFT)
        {
            dfs(map, flags, x + 1, y, IN_UP, path);  //L_DL
            dfs(map, flags, x - 1, y, IN_DOWN, path);  //L_LU
        }
        else if (direction == IN_UP)
        {
            dfs(map, flags, x, y + 1, IN_LEFT, path);  //L_UR
            dfs(map, flags, x, y - 1, IN_RIGHT, path);  //L_LU
        }
        else if (direction == IN_RIGHT)
        {
            dfs(map, flags, x - 1, y, IN_DOWN, path);  //L_UR
            dfs(map, flags, x + 1, y, IN_UP, path);  //L_RD
        }
        else
        {
            dfs(map, flags, x, y + 1, IN_LEFT, path);  //L_RD
            dfs(map, flags, x, y - 1, IN_RIGHT, path);  //L_DL
        }
    }

    flags[x][y] = false;  //取消标记

    path.pop_back();  //将当前尝试的坐标出栈
}

int main(void)
{
    vector<vector<PIPE_TYPE>> map = {
        {I_H, L_DL, I_H, L_DL},
        {L_UR, I_H, L_DL, TREE},
        {L_RD, L_DL, I_H, L_UR},
        {I_V, L_UR, L_UR, I_H},
        {L_UR, I_H, I_H, L_LU}
    };
    vector<vector<bool>> flags(map.size(), vector<bool>(map[0].size(), false));

    vector<Point> path;

    dfs(map, flags, 0, 0, IN_LEFT, path);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值