Day10:寻路算法之A*寻路算法

一、概要:

①以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[ ][ ]不兼容的

        ③点运算符和->运算符的优先级是高于*运算符的!

         ④编译错误:

 去掉之后->

 会有这样的报错->总结:在cpp中最好也是将struct 起一下别名or直接用class +public

        ⑤memset并没有将pParent置为nullptr!!

 四、其他可能做出的改进

可以采用优先队列当buffer(默认less,即队首是最大的),每次无序遍历,直接erase()即可。

Day14:C++之STL容器(2/3)__https://blog.csdn.net/zjjaibc/article/details/125402421

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Ocean__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值