P1379 八数码难题(洛谷)

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

输入格式

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

输出格式

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

输入
283104765

输出

4

思路:使用bfs+set判重。
用结构体保存初末状态步数,并且入队,向四个方向寻找下一个状态放到set集合(判重)和队列(bfs)中,一直找下去,直到找到末状态.(使用时间:7.72s)

#include<bits/stdc++.h>
using namespace std;
typedef struct node {
	char origin[10];
	int step;
}node;
char last[10];
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
queue<node>p;
set<string>vis;
void bfs()
{
	while(!p.empty())
	{
		node head=p.front();
		p.pop();
		if(!strcmp(head.origin,last))
		{
			printf("%d\n",head.step);
			return;
		}
		int pos;
		for(int i=0;head.origin[i]!='\0';i++)
		if(head.origin[i]=='0')
		{
		 	pos=i;
			break;
		}
		int x1=pos/3;
		int y1=pos%3;
		for(int i=0;i<4;i++)
		{
			int x2=x1+dir[i][0];
			int y2=y1+dir[i][1];
			if(x2>=0&&x2<3&&y2>=0&&y2<3)
			{
				node t=head;
				int pos1=x2*3+y2;
				swap(t.origin[pos],t.origin[pos1]);
				if(!vis.count(t.origin))
				{
					vis.insert(t.origin);
					t.step++;
					p.push(t);
				}
			}
		}
	}
	printf("-1\n");
}
int main()
{
	node t;
	sscanf("123804765","%s",last);
	scanf("%s",t.origin);
	t.step=0;
	p.push(t);
	vis.insert(t.origin);
	bfs();
	return 0;
 } 

方法2:使用双向bfs+map判重(时间:221ms)比上一个快34倍

思路:一开始将出状态和末状态入队,出状态和末状态都向下一层延伸,当到达重合的节点时,终止条件是(步数和为3),只有末状态标记的是3,其他状态都是1。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
map<string,int>vis;
map<string,int>step;
string origin;
string last="123804765";
void bfs()
{
	if(origin==last)
	{
		printf("0\n");
		return;
	}
	queue<string>p;
	p.push(origin);
	p.push(last);//没将last插到队列中超限33% (双向bfs) 
	while(!p.empty())
	{
		string head=p.front();
		p.pop();
		int pos;
		for(int i=0;i<head.length();i++)
		if(head[i]=='0')
		{
			pos=i;
			break;
		}
		int x=pos/3;
		int y=pos%3;
		for(int i=0;i<4;i++)
		{
			int x2=x+dir[i][0];
			int y2=y+dir[i][1];
			if(x2>=0&&x2<3&&y2>=0&&y2<3)
			{
				int pos1=x2*3+y2;
				string t=head;
				swap(t[pos],t[pos1]);
				if(!vis.count(t))
				{
					vis[t]=vis[head];
					step[t]=step[head]+1;
					p.push(t);
				}
				else if(vis[head]+vis[t]==3)
				{
					printf("%d\n",step[head]+step[t]+1);
					return ;
				}
			}
		}
	}
	printf("-1\n");
}
int main()
{
	cin>>origin;
	vis[origin]=1;//标记除终点以外状态都是1 
	vis[last]=2;//终点状态2 
	step[origin]=0;//步数 
	bfs();
	return 0;
 } 

总结:紫书P199讲的就是八数码问题,这道题的方法多种,以上仅为两种方法,第二种方法相对是最优解。也看到过0ms的最优解,膜拜~~~(c语言网的九宫重排跟这道题类似)

发布了14 篇原创文章 · 获赞 0 · 访问量 212

猜你喜欢

转载自blog.csdn.net/qq_43566782/article/details/104096822