一、概要:
①以A星为基础的寻路算法:遗传算法,蚁群算法. . .
②各参数的含义
f :最终做选择的依据、代价-> 用来评价寻路的快慢
g:从起点到当前点已经付出的代价-> 沉没成本
h:当前点到终点的预估代价
③有关沉没成本、预估代价的含义:
沉没成本 g:
小明和小红去创业,小明无工作 0 收入,小红工作每月收入2 W,小明的沉没成本就是 0元 / 月,小红的沉没成本就是 2W / 月,如果去创业,小红就相当于每个月损失2W,小明没有损失
预估代价 h:
假设小明和小红创业的收益分别为:小明1.5W /月,小红3W / 月
最终整合起来:小明去创业更合适,他的整体代价最小
④实际案例:
走一个直线的代价与斜线的代价之比为1:1.414
蓝色部分为 g 值 红色部分为 h 值 紫色部分为最佳路径
⑤需要准备的数据结构
(1)一棵树:记录整个过程中的路线、经过
(2)一个数组buffer:纳入考虑的点要找出来哪一个 f 值最小
(3)三张地图:①地图1MAP是真地图
②地图2是用来判断是否走过的
③地图3是用来判断是否放进过buffer的
⑥遇到死胡同的解决办法:
从起点(树的根节点)开始,每一步都是去找它周围能走的点(指没有走过的点,而非没有加入过buffer的点),然后入树,
(先判断之前是否放过)放到容器buffer中,找完之后,从容器buffer中挑出来 f 值最小的那一个,把它从容器中删掉,让它变成树的当前点,再循环,从当前点开始,找当前点周围能走的点,入树,一路去找. . .
⑦其他算法流程的描述:
此处的open列表就是buffer
⑧H的计算方法:
两种计算方法->本处采用曼哈顿距离
二、代码实现
#include<iostream> //有abs
#include<vector>
using namespace std;
#define ROWS 10
#define COLS 10
#define Direct_Cost 10
#define Oblique_Cost 14
bool CanWalk(const struct Point& p, bool pathmap[ROWS][COLS], bool MAP[ROWS][COLS]);
//准备->①辅助地图 ②MAP ③树节点类型 ④坐标类型 ⑤buffer存储已经检测过的点 ⑥方向的枚举类型
bool pathMap[ROWS][COLS] = { 0 };//0表示没有走过
bool pathMap_isAddBuff[ROWS][COLS] = { 0 };
bool MAP[ROWS][COLS] =
{
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },//0
{ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 },//1
{ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 },//2
{ 0, 1, 0, 1, 1, 0, 0, 0, 0, 0 },//3
{ 0, 1, 1, 0, 1, 0, 0, 1, 0, 0 },//4
{ 0, 0, 1, 1, 1, 0, 1, 1, 0, 0 },//5
{ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0 },//6
{ 1, 0, 1, 1, 1, 0, 1, 0, 0, 0 },//7
{ 0, 1, 1, 0, 0, 1, 1, 0, 0, 0 },//8
{ 0, 0, 0, 1, 1, 0, 1, 0, 0, 0 }//9
};
struct Point
{
int row;
int col;
int f, g, h;
bool operator==(const Point& a) { return (row == a.row && col == a.col); }
void getH(const Point& end);
void getF() { f = g + h; };
};
struct treeNode
{
Point data;
treeNode *pParent;
vector<treeNode*>pChild;
treeNode() = default;
treeNode(const Point& d)
{
treeNode* pNew = new treeNode;
pParent = nullptr;
data = d;
}
};
vector<treeNode*>buffer;
enum dir{UP,RIGHT,DOWN,LEFT,RIGHT_UP,RIGHT_DOWN,LEFT_DOWN,LEFT_UP};
int main()
{
Point begPos = { 0,0 }; //起点和终点
Point endPos = { 6,7 };
treeNode *pRoot=new treeNode(begPos);
pathMap_isAddBuff[pRoot->data.row][pRoot->data.col] = 1;
bool isFind = false;
treeNode *pCurrent=pRoot;
treeNode *pSearch;
while (!isFind)
{
for (int i = 0; i < 8; i++)
{
pSearch = new treeNode(pCurrent->data);//每次进来都要重置pSearch->切不能修改到pCurrent的data!!!!
switch (i)
{
case UP:pSearch->data.row--; pSearch->data.g += Direct_Cost; break;
case DOWN:pSearch->data.row++; pSearch->data.g += Direct_Cost; break;
case RIGHT:pSearch->data.col++; pSearch->data.g += Direct_Cost; break;
case LEFT:pSearch->data.col--; pSearch->data.g += Direct_Cost; break;
case RIGHT_DOWN:pSearch->data.row++; pSearch->data.col++; pSearch->data.g += Oblique_Cost; break;
case RIGHT_UP:pSearch->data.row--; pSearch->data.col++; pSearch->data.g += Oblique_Cost; break;
case LEFT_DOWN:pSearch->data.row++; pSearch->data.col--; pSearch->data.g += Oblique_Cost; break;
case LEFT_UP:pSearch->data.row--; pSearch->data.col--; pSearch->data.g += Oblique_Cost; break;
}
if (CanWalk(pSearch->data, pathMap, MAP))
{
if (pathMap_isAddBuff[pSearch->data.row][pSearch->data.col] == 0)//说明没有加入过buffer
{
buffer.push_back(pSearch);
pathMap_isAddBuff[pSearch->data.row][pSearch->data.col] =1;//加完之后,修改
}
//双向设置!->parent和child
pCurrent->pChild.push_back(pSearch);
pSearch->pParent = pCurrent;
//求出该节点的f和h
pSearch->data.getH(endPos);
pSearch->data.getF();
}
else{delete pSearch; pSearch = nullptr;}//不能走的地方就不放进去了
}
if (buffer.empty())break;//若添加完周边可能走的点之后size仍然为0,说明能走的点已经走完了
//选择出f_cost最小的(在buffer中),pCurrent更新为最小点->删除在buffer中的位置(专门应对死胡同的情况)
auto pmin = buffer.begin();
for (auto p = buffer.begin(); p != buffer.end(); p++)
{
pmin = ((( * p)->data.f < (* pmin)->data.f) ? p : pmin);
}//遍历出指向f_min位置的迭代器
pCurrent = *pmin;//更新pCurrent
pathMap[pCurrent->data.row][pCurrent->data.col] = 1;//选择的点标记走过
buffer.erase(pmin);//删除在buffer中的位置
//test
Point a = { 3,0 };
/*cout << "(" << pCurrent->data.row << "," << pCurrent->data.col << ")" << " ";
if (pCurrent->data == a)
while (1);*/
if (pCurrent->data == endPos)isFind = true;
//if (buffer.empty())break;//说明能走的点已经走完了----->7.30 不应该放在这里判断(应该在添加完之后,size仍然为0再去判断
}
if (isFind)
{
while (pCurrent)
{
cout << "找到啦!" << endl;
cout << "(" << pCurrent->data.row << "," << pCurrent->data.col << ")" << " ";
pCurrent = pCurrent->pParent;
}
}
else
{
cout << "not found ,没找到捏" << endl;
}
return 0;
}
void Point::getH(const Point& end)
{
h = abs(row - end.row) * Direct_Cost + abs(col - end.col) * Direct_Cost;//曼哈顿距离->勿忘记乘上直线代价
}
bool CanWalk(const Point& p, bool pathmap[ROWS][COLS], bool MAP[ROWS][COLS])
{
bool flag = true;
//1.越界不能走
if (p.col < 0 || p.col >= COLS || p.row < 0 || p.row >= ROWS)flag = false;
//2.放入到buffer里面的点,不能走
if (pathmap[p.row][p.col]==1)flag = false;
//3.是墙(1)不能走
if (MAP[p.row][p.col]==1)flag = false;
return flag;
}
受此样例启发:
①需要额外增加一个辅助地图,用来标记每个点是否放入过buffer,否则buffer会重复放入多次一个相同的点
②不能在while的最后用buffer.size()==0来break,因为当
在A点时候,buffer只存在B,所以current移到B,此后删除B,则size==0直接break导致错误(实际上,B还是可以往C方向走的)
---->解决:在添加完周边可能的点到buffer之后,再去判断size!!!
最终运行:
三、bug一览
①周围可以走的点加入buffer,在标记走过的时候,应该是加入buffer的点都应该标记->否则每走到下一个点的时候,buffer会增加重复已经增加过的点->导致死循环(别忘记标记)
②二维数组传参 bool* arr[ ]是与bool arr[ ][ ]不兼容的
③点运算符和->运算符的优先级是高于*运算符的!![](https://i-blog.csdnimg.cn/blog_migrate/bcc39070aab72e4efbbee44db69dfdf3.png)
④编译错误:![](https://i-blog.csdnimg.cn/blog_migrate/fa046fd8c96bb258b560e8d9bfaa5167.png)
去掉之后->
会有这样的报错->总结:在cpp中最好也是将struct 起一下别名or直接用class +public
⑤memset并没有将pParent置为nullptr!!
四、其他可能做出的改进
可以采用优先队列当buffer(默认less,即队首是最大的),每次无序遍历,直接erase()即可。
Day14:C++之STL容器(2/3)__https://blog.csdn.net/zjjaibc/article/details/125402421