题目
题目描述
现将1~8这八个自然数填入方格中,给定一个初始状态,例如为:281463750,其中空方格用数字0表示。现允许移动空格,但每次只能移动1格。试编一程序完成对于任意给定的一个目标状态,如:123804765,能够以最少步数实现从初始状态到目标状态的转换。方向优先:(左,上,下,右)
输入
两行,每行9个数,保证里面有0 第一行表示起始状态 第二行表示目标状态
输出
若干个组 3行为一组,每行3个数,表示每一步八数码移动的状态 每个数字占3个场宽
如果无解,则输出:NO solution!
样例
-
输入样例
2 8 3 1 0 4 7 6 5
1 2 3 8 0 4 7 6 5 -
输出样例
2 8 3
1 0 4
7 6 52 0 3
1 8 4
7 6 50 2 3
1 8 4
7 6 51 2 3
0 8 4
7 6 51 2 3
8 0 4
7 6 5
//域宽为3,故仅供参考
题意
给出一个0~8的九宫格(以一维方式),并给出一个目标九宫格。其中只能通过向四个方向移动0点,把原九宫格变为目标九宫格。要求输出移动0点的步骤。
题解
先讲一下主要思路。题目要求0点移动步数最少。观察发现,每次移动只能把一个数字与0交换,这样最多把一个数字向它对应的目标位置靠近1步。加入每一步移动都是有效的,这样每个状态到目标状态的移动步数,就不可能小于所有数当前位置与目标位置的曼哈顿距离之和。于是我们可以得到一个这样的估价函数: F ( i ) = s u m ( ∣ x i − x g o a l ∣ + ∣ y i − y g o a l ∣ ) F(i)=sum(|x_i-x_{goal}|+|y_i-y_{goal}|) F(i)=sum(∣xi−xgoal∣+∣yi−ygoal∣),其中 x i x_i xi, y i y_i yi表示每个数字现在的位置, x m x_m xm, y m y_m ym表示每个数字的目标位置。
下面讲讲细节。首先关于存储:由于我们的当前值、目标值都是一维的,也就是
s
[
0
]
s[0]
s[0]到
s
[
8
]
s[8]
s[8]和
g
o
a
l
[
0
]
goal[0]
goal[0]到
g
o
a
l
[
8
]
goal[8]
goal[8](
[
0
]
[
0
]
[0][0]
[0][0]到
[
2
]
[
2
]
[2][2]
[2][2]有兴趣可以去试一下),所以在走迷宫需要调用行数和列数时,需要取
x
/
3
x/3
x/3和
y
%
3
y\%3
y%3。
然后解释一下结构体:
f
a
,
p
o
s
,
d
e
p
,
e
v
e
fa,pos,dep,eve
fa,pos,dep,eve分别维护了当前状态的父亲、0点的位置、搜索的深度和当前估值,
s
[
10
]
s[10]
s[10]便是当前状态。
最后便是大家最关心的康托展开。这种操作博主也是第一次见,所以也没有深究大写的菜 。总之就是用一个无重复键值的哈希,把当前状态存下来,以避免重复。如果觉得难以理解的话,可以暂时想成10进制——比如说此时状态为:1 2 3 4 5 6 7 8 0,则此时的哈希值为123456780,所以如果遍历到这种情况则
v
i
s
[
123456780
]
=
1
vis[123456780]=1
vis[123456780]=1——大概就是这样啦,虽然不太可能,所以用康托展开,把每个状态映射到
c
o
d
e
code
code,然后判逆序对和重复。
代码
#include<iostream>
#include<cstdio>
#include<cctype>
#include<queue>
#include<iomanip>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn=2e5+10;
int last;
int dx[5]={0,0,-1,0,1};
int dy[5]={0,-1,0,1,0};//下左上右
int goal[10];
bool vis[maxn<<1];
long long power[10];
struct node
{
int fa,pos,dep,eva;
int s[10];
bool operator <(const node &t)const
{
return eva>t.eva;
}
};
node list[maxn];
priority_queue<node> q;
inline bool cmp(int *a,int *b)
{
for(int i=0;i<9;++i)
if(a[i]!=b[i]) return 1;
return 0;
}//比较当前状态与目标状态是否一致
inline int contor(int *x)
{
int code=0;
for(int i=0;i<9;++i)
{
int cnt=0;
for(int j=i+1;j<9;++j) if(x[j]<x[i]) cnt++;
code+=1ll*cnt*power[8-i];
}
if(vis[code]) return 1;
vis[code]=1;
return 0;
}//康托展开
inline void print(int *x)
{
for(int i=0;i<9;++i)
{
cout<<setw(3)<<x[i];//输出域宽为3
if((i+1)%3==0) cout<<"\n";
}
cout<<"\n";
}
inline void write(int k)
{
if(!k) return;
write(list[k].fa);
print(list[k].s);
}
inline int evaluation(int *x)
{
int ret=0;
int c[10],t[10];
for(int i=0;i<9;++i)
c[x[i]]=i,t[goal[i]]=i;
for(int i=1;i<9;++i)
ret+=abs(c[i]/3-t[i]/3)+abs(c[i]%3-t[i]%3);
return ret;
}//计算估价函数
inline void expand(node &st)
{
int x=st.pos/3;
int y=st.pos%3;
for(int i=1;i<=4;++i)
{
int newx=x+dx[i];
int newy=y+dy[i];
int newp=newx*3+newy;
if(newx<3&&newx>=0&&newy<3&&newy>=0)
{
node now=st;
now.s[newp]=st.s[st.pos];
now.s[st.pos]=st.s[newp];
if(contor(now.s)) continue;
now.fa=last;
now.pos=newp;
now.dep++;
now.eva=now.dep+evaluation(now.s);
q.push(now);
}
}
}//深搜展开
int bfs()
{
while(!q.empty())
{
node tmp=q.top();
q.pop();
list[++last]=tmp;
if(!cmp(goal,tmp.s)) return 1;
expand(tmp);
if(last>10000) break;
}
return 0;
}
int main()
{
power[0]=1;
for(int i=1;i<9;++i) power[i]=power[i-1]*1ll*i;
node st;
for(int i=0;i<9;++i)
{
cin>>st.s[i];
if(!st.s[i]) st.pos=i;
}
for(int i=0;i<9;++i) cin>>goal[i];
st.fa=0;
st.eva=evaluation(st.s);
st.dep=0;
contor(st.s);
q.push(st);
if(bfs()) write(last);
else cout<<"NO solution!";
return 0;
}