1225 八数码难题
时间限制: 1 s
空间限制: 128000 KB
题目等级 : 钻石 Diamond
题目描述 Description
Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.
问题描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入描述 Input Description
输入初试状态,一行九个数字,空格用0表示
输出描述 Output Description
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
样例输入 Sample Input
283104765
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
详见试题
A*算法
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define INF 9999999
#define MAX_N 10
struct State
{
char Data[MAX_N];
int G;
int H;
};
char strInit[MAX_N];
char strGoal[MAX_N];
vector<State> Opened;
vector<State> Closed;
int dx[]={1,0,-1,0};//右,下,左,上
int dy[]={0,1,0,-1};
int H(const char *strState)
{
int cnt=0;
for(int i=0;i<9;i++)
{
if(strState[i]!=strGoal[i]) cnt++;
}
return cnt;
}
int FindMin()
{
int mini,minx=INF;
int sz=Opened.size();
for(int i=0;i<sz;i++)
{
if(minx>Opened[i].G+Opened[i].H)
{
mini=i;
minx=Opened[i].G+Opened[i].H;
}
}
return mini;
}
bool UpdateOpened(State &state)
{
int sz=Opened.size();
for(int i=0;i<sz;i++)
{
if(!strcmp(state.Data,Opened[i].Data) && state.G<Opened[i].G)
{
Opened[i].G=state.G;
return true;
}
}
return false;
}
bool InClosed(const char *strState)
{
int sz=Closed.size();
for(int i=0;i<sz;i++)
{
if(!strcmp(strState,Closed[i].Data))
{
return true;
}
}
return false;
}
void Expand(State &state)
{
State tmp;
int y,x;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
{
if(state.Data[i*3+j]=='0')
{
y=i;x=j;
}
}
int nx,ny;
for(int i=0;i<4;i++)
{
nx=x+dx[i];ny=y+dy[i];
if(nx>=0 && nx<3 && ny>=0 && ny<3)
{
strcpy(tmp.Data,state.Data);
char ch=tmp.Data[ny*3+nx];
tmp.Data[y*3+x]=ch;
tmp.Data[ny*3+nx]='0';
tmp.G=state.G+1;
if(InClosed(tmp.Data)) continue;
if(!UpdateOpened(tmp))
{
tmp.H=H(tmp.Data);
Opened.push_back(tmp);
}
}
}
}
int main()
{
int ans=-1;
State tmp;
strcpy(strGoal,"123804765");
scanf("%s",strInit);
strcpy(tmp.Data,strInit);
tmp.G=0;
tmp.H=H(strInit);
Opened.push_back(tmp);
while(!Opened.empty())
{
int minID=FindMin();
tmp=Opened[minID];
Opened.erase(Opened.begin()+minID);
Closed.push_back(tmp);
if(!strcmp(tmp.Data,strGoal))
{
ans=tmp.G;
break;
}
Expand(tmp);
}
printf("%d\n",ans);
return 0;
}