IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> BFS&DFS进阶 -> 正文阅读

[数据结构与算法]BFS&DFS进阶

启发式算法

如果在选择节点时能充分利用与问题有关的特征信息,估计出节点的重要性,就能在搜索时选择重要性较高的节点,以利于求得最优解。这个过程称为启发式搜索。

与被解问题的某些特征值有关的控制信息(如解的出现规律、解的结构特征等)称为搜索的启发信息。它反映在评估函数中,评估函数的作用是评估待扩展各节点在问题求解中的价值,即评估节点的重要性。

评估函数:f()= g(x)+ h(x)

g(x)是从初始节点到一个节点x的实际代价。

h(x)是这个节点x到目标节点的最优路径的估计代价,体现了问题的启发式信息,h(x)称为启发式函数。

??????

?

?迭代加深A*算法

迭代加深A*算法:IDA*

设置一个限制值c

只有当节点n的子节点n'的f(n')< c时,才扩展n'节点放到栈中,并且更新c的值(取它和f(n')较小的那个值)

算法终止的条件是:·找到目标节点

·栈为空并且限制值c'= co (可以理解成是一个巨大的值,如100000000)使用IDA*是为了优化空间,节省内存。

A*代码:
#include<queue>
#include<iostream>
#include"stdlib.h"
#include<stack>
#include"math.h "
#define num 9
//伪代码
/*
1 读取初始状态和目标状态
2 将初始节点压入open表中
3 取出open表中估计值最小的节点,放入close表中
4 判断该节点是否为目标节点,不是的话,拓展该节点,将子节点放入open表中,返回上一步
5 将该节点压入栈中,并将指针指向父节点
6 如果父节点不为空,继续5
7 如果栈不为空,出栈并输出该节点
*/
using namespace std;
struct Node {
	int state[9];  //用一维数组储存方阵
	struct Node* parent;  //定义指向其父节点的指针 
	int value;  // 曼哈顿数 
	int depth;   // 深度也就是第几次移动 
	friend bool operator < (Node A,Node B) { //按照value值+depth值小的方案构造优先级队列
		return  A.value+A.depth > B.value+B.depth;//最小优先队列
	}
};

priority_queue<Node> openTable;  //优先级队列open表 
queue<Node> closeTable;     //普通的队列close表 
stack<Node> Path;         // 栈用于存储 正确的步骤 
int count1=0,count2=0; //判断逆序数奇偶是否相同 

int judge(Node& S,Node& G) {
	S.parent=NULL; S.depth=0; S.value=0;
	G.parent=NULL; G.depth=0; G.value=0;

	cout<<"请输入初始状态\n";
	for(int i=0; i<num; i++)
		cin>>S.state[i];
	cout<<"请输入目标状态\n";
	for(int i=0; i<num; i++)
		cin>>G.state[i];

	//将方阵变成线性数列后,计算初始状态和目标状态的逆序数奇偶行是否一致判断是否有解,一致则有解,否则无解。

	for(int i=0; i<=num-2; i++)
		for(int j=i+1; j<num; j++)
			if(S.state[i]>S.state[j]&&S.state[i]*S.state[j]!=0)
				count1++;

	for(int i=0; i<=num-2; i++)
		for(int j=i+1; j<num; j++)
			if(G.state[i]>G.state[j]&&G.state[i]*G.state[j]!=0)
				count2++;

	if(count1%2==count2%2)
	{
		return 1;
	}
	    return 0;
}

int value(Node A,Node G) {
	int count=0,begin[3][3],end[3][3];  //count 记录所有棋子移动到正确位置所需要的步数
	for(int i=0; i<num; i++) {
		begin[i/3][i%3]=A.state[i];
		end[i/3][i%3]=G.state[i];
	}
	for(int i=0; i<3; i++) 
		for(int j=0; j<3; j++) {
			if(begin[i][j]!=end[i][j]) {
				for(int k=0; k<3; k++)
					for(int w=0; w<3; w++)
						if(begin[i][j]==end[k][w])
							count =count+fabs(i-k)+fabs(j-w); //计算每个数字当前状态与最终状态的曼哈顿距离
			} else {
				continue;
			}
		}
		return count+A.depth;
	
}

bool equal(Node S,Node G) {
	for(int i=0; i<=8; i++) {
		if(S.state[i]!=G.state[i]) {
			return false;
		}
	}
	return true;
}
void creatNode(Node& S,Node& G) {
	//计算原状态下,空格所在的行列数,从而判断空格可以往哪个方向移动
	int blank;
	for(blank=0; blank<9&&S.state[blank]!=0; blank++); //找到空白格

	int x=blank/3,y=blank%3; //获取0所在的编号

	for(int d=0; d<4; d++) { //找到S扩展的子节点,加入open表中
		int newX=x,newY=y;
		Node tempNode;
		/*移动空白格*/
		if(d==0) newX=x-1;
		if(d==1) newY=y-1;
		if(d==2) newX=x+1;
		if(d==3) newY=y+1;

		int newBlank =newX*3+newY;  //0新的位置

		if(newX>=0 && newX<3 && newY>=0 && newY<3) { //如果移动合法
			/*交换格子的内容*/
			tempNode = S;
			tempNode.state[blank]=S.state[newBlank];
			tempNode.state[newBlank] =0;

			if(S.parent!=NULL&&(*S.parent).state[newBlank]==0) {
				continue;
			}
			/*把子节点都加入open表中*/
			tempNode.parent= &S;
			tempNode.value = value(tempNode,G);
			tempNode.depth = S.depth+1;
			openTable.push(tempNode);
		}
	}
}
int main() 
{
	Node S0,Sg;   //S0 初始状态 0 1 2 3 4 5 6 7 8  Sg最终状态 1 2 3 8 0 4 7 6 5
	if(!judge(S0,Sg)) {
		cout<<"此题无解!";
		return 0;
	}
	openTable.push(S0);   //入队 将初始节点压入open表中
	while(true) {
		closeTable.push(openTable.top());  //将open表中优先级最高(估计值最小)的元素压入close表中
		openTable.pop(); //删除open表中优先级最高的元素
		if(!equal(closeTable.back(),Sg)) { //如果当前棋局与目标棋局不相同,则拓展当前节点,
			creatNode(closeTable.back(),Sg);
		} else 
		{
			break;
		}
	}
	Node tempNode;  //临时变量暂存队前数据
	tempNode = closeTable.back();  //back 返回队列的最后一个元素即最后入队的元素
	while(tempNode.parent!=NULL) {
		Path.push(tempNode); //入栈
		tempNode = *(tempNode.parent); //指向父节点
	}
	Path.push(tempNode);
	cout<<"至少要移动"<<Path.size()-1<<"步"<<endl;
	/*输出方案*/
	while(Path.size()!=0) {
		for(int i=0; i<num; i++) {
			cout<<Path.top().state[i]<<" ";
			if((i+1)%3==0)
				cout<<endl;
		}
		Path.pop();
		cout<<"------"<<endl;
		cout<<"\n";
	}
	return 0;
}

?

如下是两个启发式搜索的链接,不懂的可以再研究一下:

https://www.bilibili.com/video/BV1gt4y1Q7dt?from=search&seid=10570594341702602487

https://www.bilibili.com/video/BV1QK4y1j7bi?from=search&seid=18017027603264843331

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-08-10 23:11:46  更:2021-08-10 23:12:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/21 2:46:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码