码迷,mamicode.com
首页 > 其他好文 > 详细

DFS

时间:2020-01-01 23:40:22      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:mat   using   cto   learn   描述   数组   ota   org   地图   

思想

从起点出发,标记走过的点,如果发现没有走过的点,随便选一个向前走,无路可走就回退。
技术图片

应用

  • 判断从V出发能否走到终点
    技术图片
    - 判断从V出发能否走到终点,若能,记录路径
Node path[MAX_LEN];     //MAX_LEN取节点总数即可
int depth;   //当前点的深度

bool Dfs(V)
{
    if (V为终点)
    {
        path[depth] = V;
        return true;
    }
    if (V为旧点)
    {
        return false;
    }

    将V标记为旧点;

    path[depth++] = V;

    对和V相邻的每个节点U
    {
        if (Dfs(U))
            return true;
    }

    --depth;   //从V走不到终点,把V排除出数组,回退到V的父节点

    return false;
}

int main()
{
    所有点标记为新点;

    depth = 0;
    if (Dfs(起点))
    {
        for (int i = 0; i <= depth; i++)
        {
            cout << path[i] << endl;
        }
    }

    return 0;
}

- 遍历图上所有节点
技术图片

邻接矩阵存储遍历复杂度\(O(n^2)\),因为对每个节点,都要判断其它所有节点是否相邻。
邻接表遍历复杂度\(O(n+e)\)

例题

1、城堡问题
给一个地图以及每个格子周围的墙所代表数字之和,求该地图有多少房间,最大房间的面积。

分析:
要先判断每个格子周围有什么墙,注意到1,2,4,8的二进制形式0001001001001000,所以只要将输入数字与1,2,4,8相与,就能知道该方块周围有什么墙。
把方块看作节点,相邻两个方块如果没有墙,就在这两节点之间连一条边,转换为图。
房间个数:图中的极大连通子图个数
极大连通子图:一个连通子图,加任意一个图中的其他点就不连通,这个子图就是极大连通子图。

具体:
对每个房间进行DFS,得到该房间所在的极大连通子图,染色所有能够到达的房间,最后统计共用了几种颜色以及每种颜色的数量。

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

int room[50][50];
int color[50][50] = { 0 };   //标记方块是否染色,初始都未被访问
int maxRoomArea = 0, roomNum = 0, curRoomArea = 0;

void Dfs(int i,int j)   //从i,j出发遍历极大连通子图
{
    if (color[i][j])
        return;

    color[i][j] = roomNum;   //该方块染色
    curRoomArea++;
    if ((room[i][j] & 1) == 0) Dfs(i, j - 1);  //没有西墙,向西走
    if ((room[i][j] & 2) == 0) Dfs(i - 1, j);
    if ((room[i][j] & 4) == 0) Dfs(i, j + 1);
    if ((room[i][j] & 8) == 0) Dfs(i + 1, j);
}

int main()
{
    int row, column;
    scanf("%d%d", &row, &column);
    for (int i = 0; i < row; i++)
        for (int j = 0; j < column; j++)
        {
            scanf("%d", &room[i][j]);
        }

    for (int i = 0; i < row; i++)
        for (int j = 0; j < column; j++)
        {
            if (!color[i][j])   //找到一个新的房间
            {
                roomNum++;
                curRoomArea = 0;
                Dfs(i, j);          //探索该房间(极大连通子图)
            }
            maxRoomArea = max(curRoomArea, maxRoomArea);
        }

    printf("%d\n%d", roomNum, maxRoomArea);

    return 0;
}

2、踩方格
递归,从\((i,j)\)出发走n步的方案数就等于先走一步,从其它三个格子走n-1步的方案数之和。
前提就是该方块没走过。

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

bool isVisited[20][20] = { 0 };

int Dfs(int i, int j, int n)
{
    int ans = 0;
    //访问过直接返回
    if (isVisited[i][j])
        return 0;
    //递归边界
    if (0 == n)
        return 1;

    isVisited[i][j] = true;

    //可以走三个方向
    ans += Dfs(i - 1, j, n - 1);
    ans += Dfs(i, j - 1, n - 1);
    ans += Dfs(i, j + 1, n - 1);

    //返回前表示当前格子可以重新被访问,以后的走法可能会访问到
    isVisited[i][j] = false;

    return ans;
}

int main()
{
    int n;
    scanf("%d", &n);

    printf("%d\n", Dfs(20, 20, n));

    return 0;
}

3、ROADS
很多时候,并不需要一条路走到黑,这就是深搜中的剪枝
技术图片

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

using namespace std;

/*存储边,不需要起点,G(i)表示从i出发*/
struct Road {
    int destination, len, toll;
};

/*邻接表存储图*/
vector<vector<Road>> G(110);

int k, n, r;
int minLen;   //探索过的最短的路径
int totalLen;   //正在探索的最短路径
int totalCost;   //正在探索的花费
int visited[110];
int minL[110][10010]; //minL[i][j]:从1走到城市i,且花了j块钱的最优路径长度

void dfs(int s)
{
    if (s == n)   //找到了路径
    {
        minLen = min(minLen, totalLen);
        return;   //强制结束函数
    }

    int len = G[s].size();
    for (int i = 0; i < len; i++)
    {
        Road r = G[s][i];

        /*判断有没有足够的钱走到r.destination*/
        if (totalCost + r.toll > k) //钱不够,试下一条边
            continue;     //可行性剪枝

        if (!visited[r.destination])
        {
            /*最优性剪枝*/
            //当前走过的路长度已经大于之前的minLen,就没必要走下去
            if (totalLen + r.len >= minLen)
                continue;

            //走到r.d时花费同样的钱走过的路长度大于之前相同花费的路长度
            if (totalLen + r.len >= minL[r.destination][totalCost + r.toll])
                continue;

            minL[r.destination][totalCost + r.toll] = totalLen + r.len;

            totalLen += r.len;
            totalCost += r.toll;
            visited[r.destination] = 1;
            dfs(r.destination);

            /*不走r.destination*/
            visited[r.destination] = 0; //换下条边之前将访问标志清0
            totalLen -= r.len;
            totalCost -= r.toll;
        }
    }
}

/*从城市1开始深搜整个图,找到所有能到达n的,选最优的*/
int main()
{
    scanf("%d%d%d", &k, &n, &r);
    
    for (int i = 0; i < r; i++)
    {
        int source;
        Road r;

        scanf("%d%d%d%d", &source, &r.destination, &r.len, &r.toll);

        if (source != r.destination)
        {
            G[source].push_back(r);
        }
    }

    memset(visited, 0, sizeof(visited));
    totalLen = 0, totalLen = 0;
    minLen = 1 << 30;   //置为无穷大
    for (int i = 0; i < 110; i++)
        for (int j = 0; j < 10010; j++)
            minL[i][j] = 1 << 30;


    visited[1] = 1;
    dfs(1);  //走完了所有路

    if (minLen < (1 << 30))
    {
        printf("%d\n", minLen);
    }
    else
        printf("-1\n");

    return 0;
}

4、生日蛋糕
技术图片技术图片
练习1
练习2
练习3

参考郭炜老师MOOC

DFS

标签:mat   using   cto   learn   描述   数组   ota   org   地图   

原文地址:https://www.cnblogs.com/EIMadrigal/p/12130895.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!