蓝桥杯 | DFS算法及例题01,02
DFS模板:
#include <iostream>
using namespace std;
bool check()
{
...
}
void dfs()
{
if (满足边界条件)
{
return;
}
for (int i = 0; i < 可扩展的路径数; i++)
{
if (check())
{
修改现场;
dfs(下一种情况);
还原现场;
}
}
}
经典DFS例题:
1.模板题——迷宫问题
题目介绍:
给定一个N*M方格的迷宫,迷宫内设置有T处障碍。给定起点坐标和终点坐标,问:每个方格最多经过一次,有多少种起点到终点的方案。在迷宫中有上下左右三种移动方式,且每次只能移动一格。
输入格式:
第一行输入N行,M列和T个障碍,第二行输入起点坐标SX,SY和终点坐标FX,FY,接下来T行,每行都为自定义的障碍物的坐标。
输出格式:
从起点坐标到终点坐标的方案总数
输入输出样例:
输入:
2 2 1
1 1 2 2
1 2
输出:
1
题目分析:
迷宫问题是拿来练习DFS和BFS的很经典的题目,迷宫问题有很多种问法,比如迷宫从起点到终点的路径有几条,最短路径是多少。
(个人:其实就是把每一步都走一遍,碰壁则回头重新开始,比如一棵大树,一根树枝一根树枝的遍历,减去不合理的枝干,所以时间复杂度会很高)
以数组map[][]来表示迷宫,当 map [ ] [ ] ==1 时为障碍点, map [ ] [ ] == 0时为正常点,设置好迷宫后开始遍历。
AC代码:
#include <iostream>
using namespace std;
const int N = 100;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0};//方向数组技巧
int n, m, T;//n行m列T障碍总数
int ans;//记录方案总数
int sx, sy, fx, fy, l, r;//起点坐标(sx,sy)终点坐标(fx,fy)障碍点坐标(l,r)
bool visited[N][N];//记录某点是否被访问过
int map[N][N];//map[i][j] == 1表示是障碍
bool check(int x, int y)//check某点是否合法
{
if (x < 1 || x > n || y < 1 || y > m) return false;//该点出界不合法
if (map[x][y]) return false;//该点是障碍点不合法
if (visited[x][y]) return false;//该点被访问过不合法
return true;//其他情况访问合法
}
void dfs(int x, int y)//dfs维护点的坐标参数
{
if (x == fx && y == fy)//满足边界条件,到达终点
{
ans++;//方案数+1
return;
}
for (int i = 0; i < 4; i++)//枚举四个方向
{
int newx = x + dx[i];
int newy = y + dy[i];
if (check(newx, newy))//该点合法
{
visited[x][y] = true;//将(x,y)设置成已访问,修改现场
dfs(newx, newy);//dfs下一个点
visited[x][y] = false;//回溯,恢复现场
}
}
}
int main()
{
cin >> n >> m >> T;
cin >> sx >> sy >> fx >> fy;
while(T--)
{
cin >> l >> r;
map[l][r] = 1;
}
dfs(sx, sy);//从起点开始搜索
cout << ans << endl;
return 0;
}
空间优化,以maze数组表示迷宫,同时可以记录迷宫中的点是否被访问(可能是减少时间复杂度,避免超时)
//空间优化
#include <iostream>
using namespace std;
const int N = 100;
int dx[4] = {0, 0, -1, 1};
int dy[4] = {1, -1, 0, 0};//方向数组
int n, m, T;
int ans;
int sx, sy, fx, fy, l, r;
//bool visited[N][N];
int map[N][N];//map[i][j] == 1表示是障碍
bool check(int x, int y)//判断某点是否可以访问
{
if (x < 1 || x > n || y < 1 || y > n) return false;
if (map[x][y] || map[x][y] == 3 ) return false;//该点是障碍点或已经访问过,不能访问
//if (visited[x][y]) return false;
return true;
}
void dfs(int x, int y)
{
if (x == fx && y == fy)
{
ans++;
return;
}
for (int i = 0; i < 4; i++)
{
int newx = x + dx[i];
int newy = y + dy[i];
if (check(newx, newy))
{
map[x][y] = 3;//标记成已访问
dfs(newx, newy);
//点(x,y)能访问说明它不是障碍点所以回溯要让map[x][y]=0
//而不是map[x][y]=1
map[x][y] = 0;
}
}
}
int main()
{
cin >> n >> m >> T;
cin >> sx >> sy >> fx >> fy;
while(T--)
{
cin >> l >> r;
map[l][r] = 1;
}
dfs(sx, sy);
cout << ans << endl;
return 0;
}
2.01背包问题
题目分析:
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 i 件物品的体积是Vi,价值是Wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式:
第一行有两个整数,N ,V ,用空格隔开,分别表示物品数量和背包容积。
接下来用 N 行,每行两个整数Vi和Wi 分别代表第 i 件物品的体积和价值。
输出格式:
输出一个整数,表示最大价值。
数据范围:
0 < N , V <=1000
0<Vi , Wi <=1000
输入样例:
4 5
1 2
2 4
3 4
4 5
输出样例:
8
题目分析:
背包问题也是一个经典问题,有很多种解法,DFS就是其中一种。
第 i 件物品无非就是选和不选两种情况,在搜索的过程中DFS函数必须要记录当前处理的物品编号index,和当前背包的容量sumi,当前的总价值sumC
如果不选择第i个物品时,此时sumi,sumC的值不变,然后去判断第i+1个物品,也就是DFS(i,sumi,sumC)。
当选择第 i 个物品时,sum 变成 sumi+w[i] ,sumC变成sumC+v[ i ] ,接着处理第 i +1 个物品,也就是DFS(i+1,sumi+W[i],sumC+V[i]。边界条件也就是把最后的一件物品也处理完,即为当n=i的时候。
当一条分支结束时,需要判断该分支最终满不满足总重量不大于背包容量,即为 sumi<=v。满足的话则更新价值maxvaule 即 maxvalue=max(maxvalue,sumC)。
AC代码:
//01背包问题的dfs版本
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010;
int n, v, maxvalue;
int w[N], c[N];
//DFS维护三个参数,正在选第index个物品,当前的总体积,当前的总价值
void dfs(int index, int sumW, int sumC)
{
if (index == n)
{
if (sumW <= v)
{
maxvalue = max(maxvalue, sumC);
}
return;
}
dfs(index + 1, sumW, sumC);//不选第index个物品
dfs(index + 1, sumW + w[index], sumC + c[index]);//选第index个物品
}
int main()
{
cin >> n >> v;
for (int i = 0; i < n; i++)
{
cin >> w[i] >> c[i];
}
dfs(0, 0, 0);//开始时对第1件物品进行选择,此时背包重量0价值也是0
cout << maxvalue << endl;
return 0;
}
如果背包问题用DFS来解决,当数据比较大的时候仍然不能使用AC,会导致超出时间限制。

添加 家长论坛微信
全部 0条评论