hello各位看官老爷, 这是菜鸡铭的第一篇博客,本人目前接触算法刚刚两个多月, 实力确实垃圾,众爱卿可能会发问: 汝如此垃圾为何还要写博客?写博客的目的: 一来是用博客记录自己做题的思路方便复习,二来因为我是初学者,在写这篇题解时可以更多的从初学者的视角来讲解,相信看官中肯定会有很多初学的bro经历过在茫茫的博客海洋中, 死都找不到一篇自己能看懂的。
本篇文章 emmmm 在别人的博客中学会了, 自己写一篇emmm,应该,算是原创? 要是不算咋整啊这玩意;
不管了 正文action:
讲题:
1是障碍0是通道 最后咱们找到的路径 要优先满足最短,还要满足字典序最小 就是能用'D'就不用'L',
ok 上强度了everybody。
这道题 可用dfs, bfs两种做法,网上大部分的答案都是dfs, 但是我更偏向于bfs, why? 因为一般来说题目说要找“最优解” 基本上都是bfs, 题目要是说找到”可行解“那就dfs, 而且bfs, 他是模板啊!!!对于初学者不要太友好,
bfs
#include <bits/stdc++.h>
using namespace std;
const int N = 5, maxn = 7;
int dirx[4] = {1,0,0,-1};
int diry[4] = {0,-1,1,0};
char dirct[4] = {'D', 'L', 'R', 'U'};
int vis[N][maxn] = {0}; //代表我是否来到过这个点
int map_[N][maxn] = {{0,0,0,0,0,0,0},{0,0,1,0,0,0,0},{0,0,0,0,1,0,0},{0,0,0,1,0,0,1},{0,1,1,0,0,0,0}};
struct node { //记录每一个点的坐标(x,y)
int x, y;
string road; //记录走到这个点需要走的路径
node (int a, int b){
x = a;
y = b;
}
};
bool yuejie (int x, int y){//判断当前的点是否出界(出地图)
if (x < 1 || y < 1 || x > 4 || y > 6){
return false;
}
else return true;
}
int main (){
queue<node>p;//队列 用来装点的
vis[1][1] = 1;//刚开始 起点我来过了,标记一下
node t (1, 1);//设置一个node类型的t 坐标是起点
t.road = "";//走到起点路径是空 (还没走呢当然是空)
p.push(t); //让起点入队列
while (!p.empty()){
node Q = p.front();
p.pop();
if (Q.x == 4 && Q.y == 6){
cout << Q.road; //到出口了// 输出路径
//bfs好在这里 只要抵达出口 就一定是最优解 下面告诉大家为什么
break;
}
for (int i = 0; i < 4; i ++){//遍历四个方向
int tx = Q.x + dirx[i];//新坐标的纵坐标 = 当前纵坐标 + dirx
int ty = Q.y + diry[i];//例如i=0时 相当于Q.x + 1, Q.y + 0,
//纵坐标+1 横坐标不变 相当于dirct[0] ('D');
if (yuejie(tx, ty) && vis[tx][ty] == 0 && map_[tx][ty] == 0){
//如果没出界 并且这个新坐标没来过 并且新坐标是道路‘0’不是障碍‘1’;
node temp(tx, ty); //设置一个node类型的temp;
temp.road = Q.road + dirct[i];//temp这个点的路径等于原来的路径加上当前方向;
p.push(temp);
vis[tx][ty] = 1;//标记该点来过;
}
}
}
return 0;
}
看到代码可能会有些蒙,咱们一步步来,咱们用的是测试用例 但是 咱们要在这个迷宫的左边界和上边界,加上零
变成酱紫 这样咱们的起点就是(1, 1)比较方便
int dirx[4] = {1,0,0,-1};
int diry[4] = {0,-1,1,0};
char dirct[4] = {'D', 'L', 'R', 'U'};
我们假设这个迷宫有一个坐标轴,x为纵轴,y为横轴,设向下为x正方向,向右为y正方向,那么我让当前的坐标的横坐标纵坐标分别加上 dirx,diry是不是就产生了新坐标,新坐标分别在当前坐标的下左右上
问题来了 为啥bfs它直接就是最优解, 它是如何运行的呢?
看这个图, tt.road 代表每走到一个新的点所走的路径, 可以发现 D 是点(2,1) DD是(3,1), DR 是(2,2) bfs在干啥呢, 它在遍历每一个可以遍历的点 走到(2,1)时, 优先向下走(DD) 然后是左但是出界,右可以(DR), 上不行,因为上面的点vis == 1来过了
接下来DDR DRR DRRU DRRUR 发现啥了吗? DD开头的路径没了!! 咋回事儿捏,DDR是点(3,2) 右边是障碍 左边是(3,1)来过了, 下边是障碍 上边有DR(2,2)了 来过了, 所以DDR(3,2)他憋死了动不了,
总结bfs就是很多条路径一起走, 能到出口的 最先到达出口的, 肯定是路径最短最优秀的;
okok 接下来dfs, dfs就很常见了,
#include <bits/stdc++.h>
using namespace std;
const int N = 5, M = 7;
int map_[N][M] = {{0,0,0,0,0,0,0},{0,0,1,0,0,0,0},{0,0,0,0,1,0,0},{0,0,0,1,0,0,1},{0,1,1,0,0,0,0}};
int vis[N][M] = {0};
int mins[N][M]; //记录走到点x,y的最小步数 初始值赋值为无穷大;
int dirx[4] = {1, 0, 0, -1};
int diry[4] = {0, -1, 1, 0};
char dirct[4] = {'D', 'L', 'R', 'U'};
char a[N * M + 10] = {'0'};
int beststep = 2e6;
string ans;
bool yuejie (int x, int y){
if (x < 1 || y < 1 || x > N -1 || y > M - 1){
return false;
}
return true;
}
void dfs (int x, int y, int step){
if (step > beststep){
return;
}
if (x == N - 1 && y == M - 1){ //到达出口
string temp;
for (int i = 0; i <= step - 1; i ++){
temp += a[i];
}
if (step < beststep){
ans = temp;
beststep = step;
}
if (step == beststep && temp < ans){
ans = temp;
}
}
for (int i = 0; i < 4; i ++){ //四个方向
int tx = x + dirx[i];
int ty = y + diry[i];
if (yuejie(tx, ty) && map_[tx][ty] == 0 && vis[tx][ty] == 0 && step + 1 <= mins[tx][ty]){
vis[tx][ty] = 1;
mins[tx][ty] = step + 1;
a[step] = dirct[i];
dfs(tx, ty, step + 1);
vis[tx][ty] = 0;
}
}
}
int main (){
memset (mins, 1, sizeof (mins));
vis[1][1] = 1;
dfs (1, 1, 0); //坐标 and 步数;
cout << ans << endl;
return 0;
}
其实跟bfs大部分是一样的,多了一个a字符数组用来存储路径, 最后到终点找到最优路径的时候将路径一个个的放到字符串里, 由于是dfs 你会找到很多个解, 这些解可能长度不一样 或者长度一样但是字典序更优秀, 这些时候我们都需要将更优秀的字符串给到ans 直到全部遍历结束 输出最优ans;
vis[tx][ty] = 1;
mins[tx][ty] = step + 1;
a[step] = dirct[i];
dfs(tx, ty, step + 1);
vis[tx][ty] = 0;
vis = 1; 标记这个点我现在来到了, 然后dfs, dfs结束之后 需要回溯, 也就是重新让这个点没走过, 这个不知道各位能不能理解, 为什么要回溯呢, 因为dfs 遍历每一种可能, 这么多种路径, 他们可能都会经过这个tx,ty这个点所以当我某次dfs结束之后就将这路径上的所有点都vis = 0;就是下次dfs寻找其他路径的时候还可能经过这些点, emmm 怎么说呢, 明显dfs要稍微难懂,考虑的可能性更多, 更细节,而bfs是模板 先是一个队列 放入起点, while循环 将队列首元素取出来 for循环遍历四个方向, 如果符合条件就让新坐标入队列, 标记这个点来过了, 最后如果到出口了就输出终点的路径,清晰简单明了,debug思路也更清晰,
所以 对于新手, 我强烈建议各位在比赛时可以多用bfs; dfs固然有好的地方 但是 我感觉 水太深我把握不住, ok today的铭少讲堂到此为止;
第一篇博客写这么长, 放在我十九年的人生中也是相当炸裂的, 以后会长期更新滴, 啊对,有问题随时指出, 感谢大佬!