思想
从起点出发,标记走过的点,如果发现没有走过的点,随便选一个向前走,无路可走就回退。
应用
- 判断从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的二进制形式0001
、0010
、0100
、1000
,所以只要将输入数字与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
%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
", 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
", minLen);
}
else
printf("-1
");
return 0;
}
参考郭炜老师MOOC