poj 1077 八数码难题

终于自己AC了这道八数码难题,用的方法是最最普通的单项BFS+Hash,这里主要的难点在于如何保存状态,其实,在八数码里所有的状态共有9!种=362880种,所以在每个转台节点,我需要一个char state[3][3]来记录当前棋盘的状态,用一个int x,y来记录当前x的位置,用char operation来记录从父状态到这个状态需要进行的操作,当然,为了记录路径,我需要记录它的父节点,然而这个父节点要怎么去记录呢,刚开始的时候对于这个问题纠结了很久,曾经直接把结构体存在队列里面,然后用指针去引用它的父节点,殊不知,当出队操作后,声明的节点就是去了意义,所以这种做法是错误的。看了别人的思路,把所有的状态节点都存在一个结构体数组里面,并且用一个count记录当钱数组里面状态的个数,因为每出来一个新状态,就把它赋值给相应的数组元素。所以,一个状态的父状态就可以指向父状态的数组下表index,到时候可以直接读取。还有一点需要注意的是进行hash时,不同的MAX取值可能会导致不同的结果,一般讲MAX取较大的素数,事实证明,用stl  <set>来存状态去重效率会低很多,用hash的话效率会高一些。对于路径的寻找,既然知道了每一状态的操作,则可以从目标状态开始将操作压栈,到时候出栈即为正确的从初始态到目标态的操作。

下面代码,进攻参考,欢迎批评指正

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<stdlib.h>
#include<queue>
#include<set>
#include<stack>
using namespace std;

#define MAX 8000003

struct node
{
	char state[3][3];
	int parent;
	int x,y;
	char operation;
}allstate[400000];
int hash[MAX];
//set<int> stateSet;
queue<int> qu;
stack<char>path;
int dirx[4]={1,-1,0,0};
int diry[4]={0,0,1,-1};


int getHash(node n)
{
	int i,j,sum=0;
	for(i=0;i<3;i++)
		for(j=0;j<3;j++)
			sum=sum*10+n.state[i][j]-'0';
	return sum%MAX;
}

void getPath(node target)
{
	node temp=target;
	while(temp.parent!=-1)
	{
		path.push(temp.operation);
		temp=allstate[temp.parent];
	}
}

void BFS(node head)
{
	memset(hash,0,sizeof(hash));
	int stateCount=0,i,hashValue;
	node outNode,newNode;
	bool isOk=false;

	hash[getHash(head)]=1;
	allstate[stateCount]=head;
	qu.push(stateCount);
	stateCount++;

	while(!qu.empty())
	{
		int index=qu.front();
		qu.pop();
		outNode=allstate[index];

		if(getHash(outNode)==3456735)
		{
			isOk=true;
			break;
		}

		int x=allstate[index].x;
		int y=allstate[index].y;

		for(i=0;i<4;i++)
		{
			int newx=x+dirx[i];
			int newy=y+diry[i];
			if(newx<0 || newx>2 || newy<0 || newy>2)continue;

			newNode=outNode;

			swap(newNode.state[x][y],newNode.state[newx][newy]);

			hashValue=getHash(newNode);
			if(hash[hashValue])continue;
			//stateSet.insert(hashValue);
			hash[hashValue]=1;

			newNode.x=newx;
			newNode.y=newy;
			newNode.parent=index;

			if(dirx[i]==0)
			{
				if(diry[i]==1)newNode.operation='r';
				else newNode.operation='l';
			}
			if(diry[i]==0)
			{
				if(dirx[i]==1)newNode.operation='d';
				else newNode.operation='u';
			}
			qu.push(stateCount);
			allstate[stateCount++]=newNode;

		}
	}
	if(!isOk)
	{
		puts("unsolvable");return;
	}
	getPath(outNode);
	while(!path.empty())
	{
		cout<<path.top();
		path.pop();
	}
	cout<<endl;

}

int main(void)
{
	int i,j;
	node head;
	for(i=0;i<3;i++)
	{
		for(j=0;j<3;j++)
		{
			cin>>head.state[i][j];
			if(head.state[i][j]=='x')
			{
				head.state[i][j]='0';
				head.x=i;
				head.y=j;
			}
		}
	}
	head.parent=-1;
	head.operation='0';
	BFS(head);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值