P1379 八数码难题

题目描述

在 3×3 的棋盘上,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字。棋盘中留有一个空格,空格用 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入格式

输入初始状态,一行九个数字,空格用 0 表示。

输出格式

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数。保证测试数据中无特殊无法到达目标状态数据。

输入输出样例

输入 #1复制

283104765

输出 #1复制

4

说明/提示

样例解释

P1379 八数码难题_第1张图片

图中标有 0 的是空格。绿色格子是空格所在位置,橙色格子是下一步可以移动到空格的位置。如图所示,用四步可以达到目标状态。

并且可以证明,不存在更优的策略。

解析:

1.最短步骤想到使用bfs,用string来记录当前的状态,遍历状态。

注意:string 下角标是从0开始的,x = dx/3 , y = dy%3; 为真实九宫格的坐标。九宫格坐标是从(0,0)开始的。

#include
#include
#include
#include
#include
#include
using namespace std;
unordered_map d;
queue q;
int dx[4] ={-1,0,1,0};
int dy[4] = {0,1,0,-1};

int bfs(string str)
{
	q.push(str);
	string end="123804765";
	while(q.size())
	{
		string s = q.front();
		q.pop();
		if(s==end) return d[s];
		int k = s.find('0');
		int x = k/3,y = k%3;
		for(int i = 0;i < 4;i++)
		{
			int a = x+dx[i],b = y + dy[i];
			if(a<0||a>=3||b<0||b>=3) continue;
			int dis = d[s];
			swap(s[k],s[a*3 + b]);
			if(!d.count(s))//交换 
				d[s] = dis+1,q.push(s);
			swap(s[k],s[a*3+b]);//还原			
		}
	}	
} 
int main()
{
	string s;
	cin >> s;
	cout << bfs(s)<

时间复杂度为:O(9^{2})

使用A*算法进行优化。

A算法给出了评价函数的定义: f(n) = g(n) + h(n)
其中,n为待评价的节点;g(n)为从初始节点s到节点n的最佳路径耗散值的估计值;h(n)为从节点n到目标节点t的最佳路径耗散值的估计值,称为启发函数;f(n)为从初始节点s经过节点n到达目标节点的最佳路径耗散值的估计值,成为评价函数,我们每次从叶节点选出f(n)最小的节点扩展。
 

#include
#include
#include
#include
#include
#include
#include
using namespace std;
unordered_map d;
string goal = "123804765";
int gx[] = {-1,0,0,0,1,2,2,2,1};
int gy[] = {-1,0,1,2,2,2,1,0,0};
int dx[4] ={-1,0,1,0};
int dy[4] = {0,1,0,-1};
int f(string s)
{ // 每个数字到目的地的坐标 
	int res = 0;
	for(int i = 0;i < 9;i++)
	{
		int t = s[i]-'0';
		if(t) res+= abs(i/3 - gx[t]) + abs(i%3-gy[t]);
	}
	return res;
}
int bfs(string str)
{
	unordered_map d;
	priority_queue> q;//默认大根堆 ,我们只需要取负数,小的数就会在上面 
	q.push({-f(str),str});
	d[str] = 0;
	while(q.size())
	{
		auto a = q.top();
		q.pop();
		string s = a.second;
		if(s== goal) return d[s]; // d 记录现实的 状态 f为理想状态下 曼哈顿距离 
		int k = s.find('0');
		int x = k /3,y = k%3;
		for(int i = 0;i < 4;i++)
		{
			int a = x+dx[i],b = y + dy[i];
			if(a<0|| a>=3||b<0|| b>=3) continue;
			string t = s;
			swap(t[k],t[a*3+b]);
			if(!d.count(t))
			{
				d[t] = d[s]+1,q.push({-(d[t] + f(t)),t});
			}
		}
	}
} 
int main()
{
	string s;
	cin >> s;
	cout << bfs(s)<

你可能感兴趣的:(搜索,c++,算法,开发语言)